From 6d9cbacc23f6e0be35a13483ecb5aa0e5f2488ea Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Sun, 1 Jul 2012 17:11:02 +0100 Subject: new spec for theme files --- alot/defaults/theme.spec | 404 +++++++---------------------------------------- 1 file changed, 54 insertions(+), 350 deletions(-) diff --git a/alot/defaults/theme.spec b/alot/defaults/theme.spec index f23d91da..3051d0ea 100644 --- a/alot/defaults/theme.spec +++ b/alot/defaults/theme.spec @@ -1,353 +1,57 @@ -[1] -[[global]] - [[[footer]]] - fg = string(default='default') - [[[body]]] - fg = string(default='default') - [[[notify_error]]] - fg = string(default='default') - [[[notify_normal]]] - fg = string(default='default') - [[[prompt]]] - fg = string(default='default') - [[[tag]]] - fg = string(default='default') - [[[tag_focus]]] - fg = string(default='default') -[[help]] - [[[text]]] - fg = string(default='default') - [[[section]]] - fg = string(default='default') - [[[title]]] - fg = string(default='default') -[[bufferlist]] - [[[focus]]] - fg = string(default='default') - [[[results_even]]] - fg = string(default='default') - [[[results_odd]]] - fg = string(default='default') -[[search]] - [[[thread]]] - fg = string(default='default') - [[[thread_authors]]] - fg = string(default='default') - [[[thread_authors_focus]]] - fg = string(default='default') - [[[thread_content]]] - fg = string(default='default') - [[[thread_content_focus]]] - fg = string(default='default') - [[[thread_date]]] - fg = string(default='default') - [[[thread_date_focus]]] - fg = string(default='default') - [[[thread_focus]]] - fg = string(default='default') - [[[thread_mailcount]]] - fg = string(default='default') - [[[thread_mailcount_focus]]] - fg = string(default='default') - [[[thread_subject]]] - fg = string(default='default') - [[[thread_subject_isunread]]] - fg = string(default='default') - [[[thread_subject_isflagged]]] - fg = string(default='default') - [[[thread_subject_focus]]] - fg = string(default='default') - [[[thread_subject_isunread_focus]]] - fg = string(default='default') - [[[thread_subject_isflagged_focus]]] - fg = string(default='default') - [[[thread_tags]]] - fg = string(default='default') - [[[thread_tags_focus]]] - fg = string(default='default') -[[thread]] - [[[attachment]]] - fg = string(default='default') - [[[attachment_focus]]] - fg = string(default='default') - [[[body]]] - fg = string(default='default') - [[[header]]] - fg = string(default='default') - [[[header_key]]] - fg = string(default='default') - [[[header_value]]] - fg = string(default='default') - [[[summary_even]]] - fg = string(default='default') - [[[summary_focus]]] - fg = string(default='default') - [[[summary_odd]]] - fg = string(default='default') -[[envelope]] - [[[body]]] - fg = string(default='default') - [[[header]]] - fg = string(default='default') - [[[header_key]]] - fg = string(default='default') - [[[header_value]]] - fg = string(default='default') - -[16] -[[global]] - [[[footer]]] - bg = string(default='default') - fg = string(default='default') - [[[body]]] - bg = string(default='default') - fg = string(default='default') - [[[notify_error]]] - bg = string(default='default') - fg = string(default='default') - [[[notify_normal]]] - bg = string(default='default') - fg = string(default='default') - [[[prompt]]] - bg = string(default='default') - fg = string(default='default') - [[[tag]]] - bg = string(default='default') - fg = string(default='default') - [[[tag_focus]]] - bg = string(default='default') - fg = string(default='default') -[[help]] - [[[text]]] - bg = string(default='default') - fg = string(default='default') - [[[section]]] - bg = string(default='default') - fg = string(default='default') - [[[title]]] - bg = string(default='default') - fg = string(default='default') -[[bufferlist]] - [[[focus]]] - bg = string(default='default') - fg = string(default='default') - [[[results_even]]] - bg = string(default='default') - fg = string(default='default') - [[[results_odd]]] - bg = string(default='default') - fg = string(default='default') -[[thread]] - [[[attachment]]] - bg = string(default='default') - fg = string(default='default') - [[[attachment_focus]]] - bg = string(default='default') - fg = string(default='default') - [[[body]]] - bg = string(default='default') - fg = string(default='default') - [[[header]]] - bg = string(default='default') - fg = string(default='default') - [[[header_key]]] - bg = string(default='default') - fg = string(default='default') - [[[header_value]]] - bg = string(default='default') - fg = string(default='default') - [[[summary_even]]] - bg = string(default='default') - fg = string(default='default') - [[[summary_focus]]] - bg = string(default='default') - fg = string(default='default') - [[[summary_odd]]] - bg = string(default='default') - fg = string(default='default') -[[envelope]] - [[[body]]] - bg = string(default='default') - fg = string(default='default') - [[[header]]] - bg = string(default='default') - fg = string(default='default') - [[[header_key]]] - bg = string(default='default') - fg = string(default='default') - [[[header_value]]] - bg = string(default='default') - fg = string(default='default') -[[search]] - [[[thread]]] - bg = string(default='default') - fg = string(default='default') - [[[thread_focus]]] - bg = string(default='default') - fg = string(default='default') - [[[thread_authors]]] - bg = string(default='default') - fg = string(default='default') - [[[thread_authors_focus]]] - bg = string(default='default') - fg = string(default='default') - [[[thread_content]]] - bg = string(default='default') - fg = string(default='default') - [[[thread_content_focus]]] - bg = string(default='default') - fg = string(default='default') - [[[thread_date]]] - bg = string(default='default') - fg = string(default='default') - [[[thread_date_focus]]] - bg = string(default='default') - fg = string(default='default') - [[[thread_mailcount]]] - bg = string(default='default') - fg = string(default='default') - [[[thread_mailcount_focus]]] - bg = string(default='default') - fg = string(default='default') - [[[thread_subject]]] - bg = string(default='default') - fg = string(default='default') - [[[thread_subject_focus]]] - bg = string(default='default') - fg = string(default='default') - [[[thread_tags]]] - bg = string(default='default') - fg = string(default='default') - [[[thread_tags_focus]]] - bg = string(default='default') - fg = string(default='default') - -[256] -[[global]] +[global] # attributes used in all modi - [[[footer]]] - bg = string(default=None) - fg = string(default=None) - [[[body]]] - bg = string(default=None) - fg = string(default=None) - [[[notify_error]]] - bg = string(default=None) - fg = string(default=None) - [[[notify_normal]]] - bg = string(default=None) - fg = string(default=None) - [[[prompt]]] - bg = string(default=None) - fg = string(default=None) - [[[tag]]] - bg = string(default=None) - fg = string(default=None) - [[[tag_focus]]] - bg = string(default=None) - fg = string(default=None) -[[help]] + footer = attrtriple + body = attrtriple + notify_error = attrtriple + notify_normal = attrtriple + prompt = attrtriple + tag = attrtriple + tag_focus = attrtriple +[help] # formatting of the `help bindings` overlay - [[[text]]] - bg = string(default=None) - fg = string(default=None) - [[[section]]] - bg = string(default=None) - fg = string(default=None) - [[[title]]] - bg = string(default=None) - fg = string(default=None) + text = attrtriple + section = attrtriple + title = attrtriple # mode specific attributes -[[bufferlist]] - [[[focus]]] - bg = string(default=None) - fg = string(default=None) - [[[results_even]]] - bg = string(default=None) - fg = string(default=None) - [[[results_odd]]] - bg = string(default=None) - fg = string(default=None) -[[search]] - [[[thread]]] - fg = string(default=None) - [[[thread_authors]]] - bg = string(default=None) - fg = string(default=None) - [[[thread_authors_focus]]] - bg = string(default=None) - fg = string(default=None) - [[[thread_content]]] - bg = string(default=None) - fg = string(default=None) - [[[thread_content_focus]]] - bg = string(default=None) - fg = string(default=None) - [[[thread_date]]] - bg = string(default=None) - fg = string(default=None) - [[[thread_date_focus]]] - bg = string(default=None) - fg = string(default=None) - [[[thread_focus]]] - bg = string(default=None) - fg = string(default=None) - [[[thread_mailcount]]] - bg = string(default=None) - fg = string(default=None) - [[[thread_mailcount_focus]]] - bg = string(default=None) - fg = string(default=None) - [[[thread_subject]]] - bg = string(default=None) - fg = string(default=None) - [[[thread_subject_focus]]] - bg = string(default=None) - fg = string(default=None) - [[[thread_tags]]] - bg = string(default=None) - fg = string(default=None) - [[[thread_tags_focus]]] - bg = string(default=None) - fg = string(default=None) -[[thread]] - [[[attachment]]] - bg = string(default=None) - fg = string(default=None) - [[[attachment_focus]]] - bg = string(default=None) - fg = string(default=None) - [[[body]]] - bg = string(default=None) - fg = string(default=None) - [[[header]]] - bg = string(default=None) - fg = string(default=None) - [[[header_key]]] - bg = string(default=None) - fg = string(default=None) - [[[header_value]]] - bg = string(default=None) - fg = string(default=None) - [[[summary_even]]] - bg = string(default=None) - fg = string(default=None) - [[[summary_focus]]] - bg = string(default=None) - fg = string(default=None) - [[[summary_odd]]] - bg = string(default=None) - fg = string(default=None) -[[envelope]] - [[[body]]] - bg = string(default=None) - fg = string(default=None) - [[[header]]] - bg = string(default=None) - fg = string(default=None) - [[[header_key]]] - bg = string(default=None) - fg = string(default=None) - [[[header_value]]] - bg = string(default=None) - fg = string(default=None) +[bufferlist] + focus = attrtriple + results_even = attrtriple + results_odd = attrtriple +[search] + [[threadline]] + normal = attrtriple + focus = attrtriple + # order subwidgets are displayed. subset of {date,mailcount,tags,authors,subject,count} + # every element listed must have its own subsection below + order = string_list(default=list(date,mailcount,tags,authors,subject)) + [[[__many__]]] + normal = attrtriple + focus = attrtriple + width_fixed = integer + width_weight = integer + alignment = align(default='right') + [[__many__]] + normal = attrtriple + focus = attrtriple + order = string_list(default=list(date,mailcount,tags,authors,subject)) + [[[__many__]]] + normal = attrtriple + focus = attrtriple + width_fixed = integer + width_weight = integer + alignment = align(default='right') +[thread] + attachment = attrtriple + attachment_focus = attrtriple + body = attrtriple + header = attrtriple + header_key = attrtriple + header_value = attrtriple + summary_even = attrtriple + summary_focus = attrtriple + summary_odd = attrtriple +[envelope] + body = attrtriple + header = attrtriple + header_key = attrtriple + header_value = attrtriple -- cgit v1.2.3 From 448d36e5e9cb49a9987caf70141b7875ce2d134e Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Sun, 1 Jul 2012 17:12:42 +0100 Subject: add attr_triple configobj check This parses triples of attribute strings into urwid.AttrSpecs see docstring for more info --- alot/settings/checks.py | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/alot/settings/checks.py b/alot/settings/checks.py index eab1253d..3665f415 100644 --- a/alot/settings/checks.py +++ b/alot/settings/checks.py @@ -3,15 +3,52 @@ # For further details see the COPYING file import mailbox import re +from urwid import AttrSpec, AttrSpecError from urlparse import urlparse from validate import VdtTypeError from validate import is_list -from validate import ValidateError +from validate import ValidateError, VdtValueTooLongError from alot import crypto from alot.errors import GPGProblem +def attr_triple(value): + """ + Check that interprets the value as `urwid.AttrSpec` triple for the colour + modes 1,16 and 256. It assumes a <6 tuple of attribute strings for + mono foreground, mono background, 16c fg, 16c bg, 256 fg and 256 bg + respectively. If any of these are missing, we downgrade to the next + lower available pair, defaulting to 'default'. + + :raises: VdtValueTooLongError, VdtTypeError + :rtype: triple of `urwid.AttrSpec` + """ + fg = bg = 'default' + keys = ['dfg', 'dbg', '1fg', '1bg', '16fg', '16bg', '256fg', '256bg'] + acc = {} + if not isinstance(value, (list, tuple)): + value = value, + if len(value) > 6: + raise VdtValueTooLongError(value) + # ensure we have exactly 6 attribute strings + attrstrings = (value + (6 - len(value)) * [None])[:6] + # add fallbacks for the empty list + attrstrings = (2 * ['default']) + attrstrings + for i, value in enumerate(attrstrings): + if value: + acc[keys[i]] = value + else: + acc[keys[i]] = acc[keys[i - 2]] + try: + mono = AttrSpec(acc['1fg'], acc['1bg'], 1) + normal = AttrSpec(acc['16fg'], acc['16bg'], 16) + high = AttrSpec(acc['256fg'], acc['256bg'], 256) + except AttrSpecError, e: + raise ValidateError(e.message) + return mono, normal, high + + def mail_container(value): """ Check that the value points to a valid mail container, -- cgit v1.2.3 From 02cafd8830acada13126d5d61544ac289cee27eb Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Sun, 1 Jul 2012 17:16:50 +0100 Subject: add align_mode configobj check that checks if value is valid align mode string --- alot/settings/checks.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/alot/settings/checks.py b/alot/settings/checks.py index 3665f415..dc68827a 100644 --- a/alot/settings/checks.py +++ b/alot/settings/checks.py @@ -7,7 +7,7 @@ from urwid import AttrSpec, AttrSpecError from urlparse import urlparse from validate import VdtTypeError from validate import is_list -from validate import ValidateError, VdtValueTooLongError +from validate import ValidateError, VdtValueTooLongError, VdtValueError from alot import crypto from alot.errors import GPGProblem @@ -49,6 +49,14 @@ def attr_triple(value): return mono, normal, high +def align_mode(value): + """ + test if value is one of 'left', 'right' or 'center' + """ + if value not in ['left', 'right', 'center']: + raise VdtValueError + return value + def mail_container(value): """ Check that the value points to a valid mail container, -- cgit v1.2.3 From 8344e08a8e445870ddc6cc5d075b6a080d285684 Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Sun, 1 Jul 2012 22:51:09 +0100 Subject: add theme converter script --- extra/theme_convert.py | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100755 extra/theme_convert.py diff --git a/extra/theme_convert.py b/extra/theme_convert.py new file mode 100755 index 00000000..1d30db73 --- /dev/null +++ b/extra/theme_convert.py @@ -0,0 +1,74 @@ +#!/usr/bin/python + +from configobj import ConfigObj +import argparse +import sys + + +def get_leaf_value(cfg, path, fallback=''): + if len(path) == 1: + if isinstance(cfg, ConfigObj): + if path[0] not in cfg.scalars: + return fallback + else: + return cfg[path[0]] + else: + if path[0] not in cfg: + return fallback + else: + return cfg[path[0]] + else: + scfg = cfg[path[0]] + sp = path[1:] + return get_leaf_value(scfg, sp, fallback) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description='update alot theme files') + parser.add_argument('themefile', type=argparse.FileType('r'), + help='theme file to convert') + parser.add_argument('-o', type=argparse.FileType('w'), dest='out', + help='destination', default=sys.stdout) + args = parser.parse_args() + + old = ConfigObj(args.themefile) + out = args.out + + def lookup(path): + values = [] + for c in ['1', '16', '256']: + values.append(get_leaf_value(old, [c] + path + ['fg'])) + values.append(get_leaf_value(old, [c] + path + ['bg'])) + values = map(lambda s: '\'' + s + '\'', values) + return ','.join(values) + + for bmode in ['global', 'help', 'bufferlist', 'thread', 'envelope']: + out.write('[%s]\n' % bmode) + for themable in old['1'][bmode].sections: + out.write(' %s = %s\n' % (themable, lookup([bmode, themable]))) + + out.write('[search]\n') + out.write(' [[threadline]]\n') + + out.write(' ' * 8 + 'normal = %s\n' % lookup(['search', 'thread'])) + out.write(' ' * 8 + 'focus = %s\n' % lookup(['search', 'thread_focus'])) + out.write(' ' * 8 + 'order = date,mailcount,tags,authors,subject\n') + + out.write(' ' * 8 + '[[date]]\n') + out.write(' ' * 12 + 'normal = %s\n' % lookup(['search', 'thread_date'])) + out.write(' ' * 12 + 'focus = %s\n' % lookup(['search', 'thread_date_focus'])) + out.write(' ' * 8 + '[[mailcount]]\n') + out.write(' ' * 12 + 'normal = %s\n' % lookup(['search', 'thread_mailcount'])) + out.write(' ' * 12 + 'focus = %s\n' % lookup(['search', 'thread_mailcount_focus'])) + out.write(' ' * 8 + '[[tags]]\n') + out.write(' ' * 12 + 'normal = %s\n' % lookup(['search', 'thread_tags'])) + out.write(' ' * 12 + 'focus = %s\n' % lookup(['search', 'thread_tags_focus'])) + out.write(' ' * 8 + '[[authors]]\n') + out.write(' ' * 12 + 'normal = %s\n' % lookup(['search', 'thread_authors'])) + out.write(' ' * 12 + 'focus = %s\n' % lookup(['search', 'thread_authors_focus'])) + out.write(' ' * 8 + '[[subject]]\n') + out.write(' ' * 12 + 'normal = %s\n' % lookup(['search', 'thread_subject'])) + out.write(' ' * 12 + 'focus = %s\n' % lookup(['search', 'thread_subject_focus'])) + out.write(' ' * 8 + '[[content]]\n') + out.write(' ' * 12 + 'normal = %s\n' % lookup(['search', 'thread_content'])) + out.write(' ' * 12 + 'focus = %s\n' % lookup(['search', 'thread_content_focus'])) -- cgit v1.2.3 From 5a898a8e1c7592cccb1ba435f4136123026e37df Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Tue, 3 Jul 2012 21:14:54 +0100 Subject: update Theme constructor It just needs to store the already parsed AttrSpec objects: requested attributes are simply looked up in the configobj object --- alot/settings/theme.py | 46 ++++++---------------------------------------- 1 file changed, 6 insertions(+), 40 deletions(-) diff --git a/alot/settings/theme.py b/alot/settings/theme.py index c6c36708..1585cff9 100644 --- a/alot/settings/theme.py +++ b/alot/settings/theme.py @@ -5,6 +5,8 @@ import os from urwid import AttrSpec, AttrSpecError from utils import read_config +from checks import align_mode +from checks import attr_triple from errors import ConfigError DEFAULTSPATH = os.path.join(os.path.dirname(__file__), '..', 'defaults') @@ -19,45 +21,9 @@ class Theme(object): :raises: :class:`~alot.settings.errors.ConfigError` """ self._spec = os.path.join(DEFAULTSPATH, 'theme.spec') - self._config = read_config(path, self._spec) - self.attributes = self._parse_attributes(self._config) - - def _parse_attributes(self, c): - """ - parse a (previously validated) valid theme file - into urwid AttrSpec attributes for internal use. - - :param c: config object for theme file - :type c: `configobj.ConfigObj` - :raises: `ConfigError` - """ - - attributes = {} - for sec in c.sections: - try: - colours = int(sec) - except ValueError: - err_msg = 'section name %s is not a valid colour mode' - raise ConfigError(err_msg % sec) - attributes[colours] = {} - for mode in c[sec].sections: - attributes[colours][mode] = {} - for themable in c[sec][mode].sections: - block = c[sec][mode][themable] - fg = block['fg'] - if colours == 1: - bg = 'default' - else: - bg = block['bg'] - if colours == 256: - fg = fg or c['16'][mode][themable][fg] - bg = bg or c['16'][mode][themable][bg] - try: - att = AttrSpec(fg, bg, colours) - except AttrSpecError, e: - raise ConfigError(e) - attributes[colours][mode][themable] = att - return attributes + self._config = read_config(path, self._spec, + checks={'align': align_mode, + 'attrtriple': attr_triple}) def get_attribute(self, mode, name, colourmode): """ @@ -70,4 +36,4 @@ class Theme(object): :param colourmode: colour mode; in [1, 16, 256] :type colourmode: int """ - return self.attributes[colourmode][mode][name] + return self._config[mode][name][[1, 16, 256].index(colourmode)] -- cgit v1.2.3 From e7adb2af1138e858b42ffd50b5caa5ee9fe597fe Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Tue, 3 Jul 2012 21:16:19 +0100 Subject: converted default.theme to new format --- alot/defaults/default.theme | 408 ++++++-------------------------------------- 1 file changed, 54 insertions(+), 354 deletions(-) diff --git a/alot/defaults/default.theme b/alot/defaults/default.theme index e5781912..944e1423 100644 --- a/alot/defaults/default.theme +++ b/alot/defaults/default.theme @@ -1,354 +1,54 @@ -[1] -[[global]] - [[[footer]]] - fg = 'standout' - [[[body]]] - fg = 'default' - [[[notify_error]]] - fg = 'standout' - [[[notify_normal]]] - fg = 'default' - [[[prompt]]] - fg = 'default' - [[[tag]]] - fg = 'default' - [[[tag_focus]]] - fg = 'standout, bold' -[[help]] - [[[text]]] - fg = 'default' - [[[section]]] - fg = 'underline' - [[[title]]] - fg = 'standout' -[[bufferlist]] - [[[focus]]] - fg = 'standout' - [[[results_even]]] - fg = 'default' - [[[results_odd]]] - fg = 'default' -[[search]] - [[[thread]]] - fg = 'default' - [[[thread_authors]]] - fg = 'default,underline' - [[[thread_authors_focus]]] - fg = 'standout' - [[[thread_content]]] - fg = 'default' - [[[thread_content_focus]]] - fg = 'standout' - [[[thread_date]]] - fg = 'default' - [[[thread_date_focus]]] - fg = 'standout' - [[[thread_focus]]] - fg = 'standout' - [[[thread_mailcount]]] - fg = 'default' - [[[thread_mailcount_focus]]] - fg = 'standout' - [[[thread_subject]]] - fg = 'default' - [[[thread_subject_isunread]]] - fg = 'bold' - [[[thread_subject_isflagged]]] - fg = 'underline' - [[[thread_subject_focus]]] - fg = 'standout' - [[[thread_subject_isunread_focus]]] - fg = 'standout,bold' - [[[thread_subject_isflagged_focus]]] - fg = 'standout,underline' - [[[thread_tags]]] - fg = 'bold' - [[[thread_tags_focus]]] - fg = 'standout' -[[thread]] - [[[attachment]]] - fg = 'default' - [[[attachment_focus]]] - fg = 'underline' - [[[body]]] - fg = 'default' - [[[header]]] - fg = 'default' - [[[header_key]]] - fg = 'default' - [[[header_value]]] - fg = 'default' - [[[summary_even]]] - fg = 'default' - [[[summary_focus]]] - fg = 'standout' - [[[summary_odd]]] - fg = 'default' -[[envelope]] - [[[body]]] - fg = 'default' - [[[header]]] - fg = 'default' - [[[header_key]]] - fg = 'default' - [[[header_value]]] - fg = 'default' -[16] -[[global]] - [[[footer]]] - bg = 'dark blue' - fg = 'light green' - [[[body]]] - bg = 'default' - fg = 'default' - [[[notify_error]]] - bg = 'dark red' - fg = 'white' - [[[notify_normal]]] - bg = 'dark gray' - fg = 'light gray' - [[[prompt]]] - bg = 'black' - fg = 'light gray' - [[[tag]]] - bg = 'black' - fg = 'brown' - [[[tag_focus]]] - bg = 'dark gray' - fg = 'white' -[[help]] - [[[text]]] - bg = 'dark gray' - fg = 'default' - [[[section]]] - bg = 'dark gray' - fg = 'bold,underline' - [[[title]]] - bg = 'dark blue' - fg = 'white' -[[bufferlist]] - [[[focus]]] - bg = 'dark gray' - fg = 'white' - [[[results_even]]] - bg = 'black' - fg = 'light gray' - [[[results_odd]]] - bg = 'black' - fg = 'light gray' -[[thread]] - [[[attachment]]] - bg = 'dark gray' - fg = 'light gray' - [[[attachment_focus]]] - bg = 'light green' - fg = 'light gray' - [[[body]]] - bg = 'default' - fg = 'light gray' - [[[header]]] - bg = 'dark gray' - fg = 'white' - [[[header_key]]] - bg = 'dark gray' - fg = 'white' - [[[header_value]]] - bg = 'dark gray' - fg = 'light gray' - [[[summary_even]]] - bg = 'light blue' - fg = 'white' - [[[summary_focus]]] - bg = 'dark cyan' - fg = 'white' - [[[summary_odd]]] - bg = 'dark blue' - fg = 'white' -[[envelope]] - [[[body]]] - bg = 'default' - fg = 'light gray' - [[[header]]] - bg = 'dark gray' - fg = 'white' - [[[header_key]]] - bg = 'dark gray' - fg = 'white' - [[[header_value]]] - bg = 'dark gray' - fg = 'light gray' -[[search]] - [[[thread]]] - bg = 'default' - fg = 'default' - [[[thread_authors]]] - bg = 'default' - fg = 'dark green' - [[[thread_authors_focus]]] - bg = 'dark gray' - fg = 'dark green,bold' - [[[thread_content]]] - bg = 'default' - fg = 'dark gray' - [[[thread_content_focus]]] - bg = 'dark gray' - fg = 'black' - [[[thread_date]]] - bg = 'default' - fg = 'light gray' - [[[thread_date_focus]]] - bg = 'dark gray' - fg = 'light gray' - [[[thread_focus]]] - bg = 'dark gray' - fg = 'light gray' - [[[thread_mailcount]]] - bg = 'default' - fg = 'light gray' - [[[thread_mailcount_focus]]] - bg = 'dark gray' - fg = 'light gray' - [[[thread_subject]]] - bg = 'default' - fg = 'light gray' - [[[thread_subject_focus]]] - bg = 'dark gray' - fg = 'light gray' - [[[thread_tags]]] - bg = 'default' - fg = 'brown' - [[[thread_tags_focus]]] - bg = 'dark gray' - fg = 'yellow,bold' - -[256] -[[global]] - # attributes used in all modi - [[[footer]]] - bg = '#006' - fg = 'white' - [[[body]]] - bg = 'default' - fg = 'default' - [[[notify_error]]] - bg = 'dark red' - fg = 'white' - [[[notify_normal]]] - bg = '#68a' - fg = 'light gray' - [[[prompt]]] - bg = 'g10' - fg = 'light gray' - [[[tag]]] - bg = 'default' - fg = 'brown' - [[[tag_focus]]] - bg = '#68a' - fg = '#ffa' -[[help]] - # formating of the `help bindings` overlay - [[[text]]] - bg = 'g35' - fg = 'default' - [[[section]]] - bg = 'g35' - fg = 'bold,underline' - [[[title]]] - bg = 'g35' - fg = 'white,bold,underline' -# mode specific attributes -[[bufferlist]] - [[[focus]]] - bg = '#68a' - fg = '#ffa' - [[[results_even]]] - bg = 'g3' - fg = 'default' - [[[results_odd]]] - bg = 'default' - fg = 'default' -[[search]] - [[[thread]]] - bg = 'default' - fg = '#6d6' - - [[[thread_authors]]] - bg = 'default' - fg = '#6d6' - [[[thread_authors_focus]]] - bg = '#68a' - fg = '#8f6' - [[[thread_content]]] - bg = 'default' - fg = '#866' - [[[thread_content_focus]]] - bg = '#68a' - fg = '#866' - [[[thread_date]]] - bg = 'default' - fg = 'g58' - [[[thread_date_focus]]] - bg = '#68a' - fg = 'g89' - [[[thread_focus]]] - bg = '#68a' - fg = 'white' - [[[thread_mailcount]]] - bg = 'default' - fg = 'light gray' - [[[thread_mailcount_focus]]] - bg = '#68a' - fg = 'g89' - [[[thread_subject]]] - bg = 'default' - fg = 'g58' - [[[thread_subject_focus]]] - bg = '#68a' - fg = 'g89' - [[[thread_tags]]] - bg = 'default' - fg = '#a86' - [[[thread_tags_focus]]] - bg = '#68a' - fg = '#ff8' -[[thread]] - [[[attachment]]] - bg = 'dark gray' - fg = 'light gray' - [[[attachment_focus]]] - bg = 'light green' - fg = 'light gray' - [[[body]]] - bg = 'default' - fg = 'light gray' - [[[header]]] - bg = 'dark gray' - fg = 'white' - [[[header_key]]] - bg = 'dark gray' - fg = 'white' - [[[header_value]]] - bg = 'dark gray' - fg = 'light gray' - [[[summary_even]]] - bg = '#068' - fg = 'white' - [[[summary_focus]]] - bg = 'g58' - fg = '#ff8' - [[[summary_odd]]] - bg = '#006' - fg = 'white' -[[envelope]] - [[[body]]] - bg = 'default' - fg = 'light gray' - [[[header]]] - bg = 'dark gray' - fg = 'white' - [[[header_key]]] - bg = 'dark gray' - fg = 'white' - [[[header_value]]] - bg = 'dark gray' - fg = 'light gray' +[global] + footer = 'standout','','light green','dark blue','white','#006' + body = 'default','','default','default','default','default' + notify_error = 'standout','','white','dark red','white','dark red' + notify_normal = 'default','','light gray','dark gray','light gray','#68a' + prompt = 'default','','light gray','black','light gray','g10' + tag = 'default','','brown','black','brown','default' + tag_focus = 'standout, bold','','white','dark gray','#ffa','#68a' +[help] + text = 'default','','default','dark gray','default','g35' + section = 'underline','','bold,underline','dark gray','bold,underline','g35' + title = 'standout','','white','dark blue','white,bold,underline','g35' +[bufferlist] + focus = 'standout','','white','dark gray','#ffa','#68a' + results_even = 'default','','light gray','black','default','g3' + results_odd = 'default','','light gray','black','default','default' +[thread] + attachment = 'default','','light gray','dark gray','light gray','dark gray' + attachment_focus = 'underline','','light gray','light green','light gray','light green' + body = 'default','','light gray','default','light gray','default' + header = 'default','','white','dark gray','white','dark gray' + header_key = 'default','','white','dark gray','white','dark gray' + header_value = 'default','','light gray','dark gray','light gray','dark gray' + summary_even = 'default','','white','light blue','white','#068' + summary_focus = 'standout','','white','dark cyan','#ff8','g58' + summary_odd = 'default','','white','dark blue','white','#006' +[envelope] + body = 'default','','light gray','default','light gray','default' + header = 'default','','white','dark gray','white','dark gray' + header_key = 'default','','white','dark gray','white','dark gray' + header_value = 'default','','light gray','dark gray','light gray','dark gray' +[search] + [[threadline]] + normal = 'default','','default','default','#6d6','default' + focus = 'standout','','light gray','dark gray','white','#68a' + order = date,mailcount,tags,authors,subject + [[date]] + normal = 'default','','light gray','default','g58','default' + focus = 'standout','','light gray','dark gray','g89','#68a' + [[mailcount]] + normal = 'default','','light gray','default','light gray','default' + focus = 'standout','','light gray','dark gray','g89','#68a' + [[tags]] + normal = 'bold','','brown','default','#a86','default' + focus = 'standout','','yellow,bold','dark gray','#ff8','#68a' + [[authors]] + normal = 'default,underline','','dark green','default','#6d6','default' + focus = 'standout','','dark green,bold','dark gray','#8f6','#68a' + [[subject]] + normal = 'default','','light gray','default','g58','default' + focus = 'standout','','light gray','dark gray','g89','#68a' + [[content]] + normal = 'default','','dark gray','default','#866','default' + focus = 'standout','','black','dark gray','#866','#68a' -- cgit v1.2.3 From 1628e8809f215955e5783228ff2aa0a35f48aa9a Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Tue, 3 Jul 2012 21:42:34 +0100 Subject: add AttrFlipWidget An AttrMap that can remember attributes to set --- alot/settings/__init__.py | 4 ++++ alot/widgets.py | 12 ++++++++++++ 2 files changed, 16 insertions(+) diff --git a/alot/settings/__init__.py b/alot/settings/__init__.py index eaa8cd16..8eece89e 100644 --- a/alot/settings/__init__.py +++ b/alot/settings/__init__.py @@ -21,6 +21,8 @@ from utils import read_config from checks import force_list from checks import mail_container from checks import gpg_key +from checks import attr_triple +from checks import align_mode from theme import Theme @@ -62,6 +64,8 @@ class SettingsManager(object): newconfig = read_config(path, spec, checks={'mail_container': mail_container, 'force_list': force_list, + 'align': align_mode, + 'attrtriple': attr_triple, 'gpg_key_hint': gpg_key}) self._config.merge(newconfig) diff --git a/alot/widgets.py b/alot/widgets.py index e084d0bf..e98fbf7b 100644 --- a/alot/widgets.py +++ b/alot/widgets.py @@ -14,6 +14,18 @@ import time from alot.db.utils import decode_header +class AttrFlipWidget(urwid.AttrMap): + """ + An AttrMap that can remember attributes to set + """ + def __init__(self, w, maps, init_map='normal'): + self.maps = maps + urwid.AttrMap.__init__(self, w, maps[init_map]) + + def set_map(self, attrstring): + self.set_attr_map({None: self.maps[attrstring]}) + + class DialogBox(urwid.WidgetWrap): def __init__(self, body, title, bodyattr=None, titleattr=None): self.body = urwid.LineBox(body) -- cgit v1.2.3 From 9965080461392e56780e58a04dcbd5688c94d3bf Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Wed, 4 Jul 2012 22:01:26 +0100 Subject: fix missing brackets in theme files --- alot/defaults/default.theme | 12 ++++++------ extra/theme_convert.py | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/alot/defaults/default.theme b/alot/defaults/default.theme index 944e1423..253464a0 100644 --- a/alot/defaults/default.theme +++ b/alot/defaults/default.theme @@ -34,21 +34,21 @@ normal = 'default','','default','default','#6d6','default' focus = 'standout','','light gray','dark gray','white','#68a' order = date,mailcount,tags,authors,subject - [[date]] + [[[date]]] normal = 'default','','light gray','default','g58','default' focus = 'standout','','light gray','dark gray','g89','#68a' - [[mailcount]] + [[[mailcount]]] normal = 'default','','light gray','default','light gray','default' focus = 'standout','','light gray','dark gray','g89','#68a' - [[tags]] + [[[tags]]] normal = 'bold','','brown','default','#a86','default' focus = 'standout','','yellow,bold','dark gray','#ff8','#68a' - [[authors]] + [[[authors]]] normal = 'default,underline','','dark green','default','#6d6','default' focus = 'standout','','dark green,bold','dark gray','#8f6','#68a' - [[subject]] + [[[subject]]] normal = 'default','','light gray','default','g58','default' focus = 'standout','','light gray','dark gray','g89','#68a' - [[content]] + [[[content]]] normal = 'default','','dark gray','default','#866','default' focus = 'standout','','black','dark gray','#866','#68a' diff --git a/extra/theme_convert.py b/extra/theme_convert.py index 1d30db73..45446bed 100755 --- a/extra/theme_convert.py +++ b/extra/theme_convert.py @@ -54,21 +54,21 @@ if __name__ == "__main__": out.write(' ' * 8 + 'focus = %s\n' % lookup(['search', 'thread_focus'])) out.write(' ' * 8 + 'order = date,mailcount,tags,authors,subject\n') - out.write(' ' * 8 + '[[date]]\n') + out.write(' ' * 8 + '[[[date]]]\n') out.write(' ' * 12 + 'normal = %s\n' % lookup(['search', 'thread_date'])) out.write(' ' * 12 + 'focus = %s\n' % lookup(['search', 'thread_date_focus'])) - out.write(' ' * 8 + '[[mailcount]]\n') + out.write(' ' * 8 + '[[[mailcount]]]\n') out.write(' ' * 12 + 'normal = %s\n' % lookup(['search', 'thread_mailcount'])) out.write(' ' * 12 + 'focus = %s\n' % lookup(['search', 'thread_mailcount_focus'])) - out.write(' ' * 8 + '[[tags]]\n') + out.write(' ' * 8 + '[[[tags]]]\n') out.write(' ' * 12 + 'normal = %s\n' % lookup(['search', 'thread_tags'])) out.write(' ' * 12 + 'focus = %s\n' % lookup(['search', 'thread_tags_focus'])) - out.write(' ' * 8 + '[[authors]]\n') + out.write(' ' * 8 + '[[[authors]]]\n') out.write(' ' * 12 + 'normal = %s\n' % lookup(['search', 'thread_authors'])) out.write(' ' * 12 + 'focus = %s\n' % lookup(['search', 'thread_authors_focus'])) - out.write(' ' * 8 + '[[subject]]\n') + out.write(' ' * 8 + '[[[subject]]]\n') out.write(' ' * 12 + 'normal = %s\n' % lookup(['search', 'thread_subject'])) out.write(' ' * 12 + 'focus = %s\n' % lookup(['search', 'thread_subject_focus'])) - out.write(' ' * 8 + '[[content]]\n') + out.write(' ' * 8 + '[[[content]]]\n') out.write(' ' * 12 + 'normal = %s\n' % lookup(['search', 'thread_content'])) out.write(' ' * 12 + 'focus = %s\n' % lookup(['search', 'thread_content_focus'])) -- cgit v1.2.3 From 601a2a735ad32d5dd910f1d2f20f8b7b15fb1f58 Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Thu, 5 Jul 2012 21:14:55 +0100 Subject: add width_tuple configobj check --- alot/settings/checks.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/alot/settings/checks.py b/alot/settings/checks.py index dc68827a..eff1370e 100644 --- a/alot/settings/checks.py +++ b/alot/settings/checks.py @@ -57,6 +57,26 @@ def align_mode(value): raise VdtValueError return value + +def width_tuple(value): + """ + test if value is a valid width indicator (for a sub-widget in a column). + This can either be + ('fit', min, max): use the length actually needed for the content, padded to + use at least width min, and cut of at width max. Here, min + and max are positive integers or 0 to disable the boundary. + ('weight',n): have it relative weight of n compared to other columns. + Here, n is an int. + """ + if value is None: + value = 'fit',0,0 + elif not isinstance(value, (list, tuple)): + raise VdtTypeError(value) + elif value[0] not in ['fit', 'weight']: + raise VdtTypeError(value) + return value + + def mail_container(value): """ Check that the value points to a valid mail container, -- cgit v1.2.3 From a011bb7391ade12c2662e344bcbe1b866bd1e3fb Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Thu, 5 Jul 2012 21:15:11 +0100 Subject: use widthtuple in theme spec and default theme --- alot/defaults/default.theme | 3 +++ alot/defaults/theme.spec | 6 ++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/alot/defaults/default.theme b/alot/defaults/default.theme index 253464a0..e66b5c5d 100644 --- a/alot/defaults/default.theme +++ b/alot/defaults/default.theme @@ -37,15 +37,18 @@ [[[date]]] normal = 'default','','light gray','default','g58','default' focus = 'standout','','light gray','dark gray','g89','#68a' + width = 'fit',10,10 [[[mailcount]]] normal = 'default','','light gray','default','light gray','default' focus = 'standout','','light gray','dark gray','g89','#68a' + width = 'fit',5,5 [[[tags]]] normal = 'bold','','brown','default','#a86','default' focus = 'standout','','yellow,bold','dark gray','#ff8','#68a' [[[authors]]] normal = 'default,underline','','dark green','default','#6d6','default' focus = 'standout','','dark green,bold','dark gray','#8f6','#68a' + width = 'fit',0,30 [[[subject]]] normal = 'default','','light gray','default','g58','default' focus = 'standout','','light gray','dark gray','g89','#68a' diff --git a/alot/defaults/theme.spec b/alot/defaults/theme.spec index 3051d0ea..825c2060 100644 --- a/alot/defaults/theme.spec +++ b/alot/defaults/theme.spec @@ -27,8 +27,7 @@ [[[__many__]]] normal = attrtriple focus = attrtriple - width_fixed = integer - width_weight = integer + width = widthtuple(default=None) alignment = align(default='right') [[__many__]] normal = attrtriple @@ -37,8 +36,7 @@ [[[__many__]]] normal = attrtriple focus = attrtriple - width_fixed = integer - width_weight = integer + width = widthtuple(default=None) alignment = align(default='right') [thread] attachment = attrtriple -- cgit v1.2.3 From 10aea956a049c3e3fb9ab2c81e49cabf666c18f1 Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Thu, 5 Jul 2012 21:57:40 +0100 Subject: use widthtuple in theme spec --- alot/defaults/theme.spec | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/alot/defaults/theme.spec b/alot/defaults/theme.spec index 825c2060..f3814aa5 100644 --- a/alot/defaults/theme.spec +++ b/alot/defaults/theme.spec @@ -27,7 +27,8 @@ [[[__many__]]] normal = attrtriple focus = attrtriple - width = widthtuple(default=None) + #width_fixed = integer + #width_weight = integer alignment = align(default='right') [[__many__]] normal = attrtriple @@ -36,7 +37,8 @@ [[[__many__]]] normal = attrtriple focus = attrtriple - width = widthtuple(default=None) + #width_fixed = integer + #width_weight = integer alignment = align(default='right') [thread] attachment = attrtriple -- cgit v1.2.3 From f3cb2284d664dcc9da247ccc20df762726a9555c Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Thu, 5 Jul 2012 21:58:44 +0100 Subject: pass on get_threadline_theming to theme in settings --- alot/settings/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/alot/settings/__init__.py b/alot/settings/__init__.py index 8eece89e..130feb8d 100644 --- a/alot/settings/__init__.py +++ b/alot/settings/__init__.py @@ -229,6 +229,10 @@ class SettingsManager(object): colours = int(self._config.get('colourmode')) return self._theme.get_attribute(mode, name, colours) + def get_threadline_theming(self, thread): + colours = int(self._config.get('colourmode')) + return self._theme.get_threadline_structure(thread, colours) + def get_tagstring_representation(self, tag): """ looks up user's preferred way to represent a given tagstring -- cgit v1.2.3 From 321a70e21e8485a03bdad516ecc48e4756a0039f Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Thu, 5 Jul 2012 22:00:34 +0100 Subject: rewrite Theme class its constructor makes sure that 'threadline' subsections of 'search' section actually have subsections defined for all parts listed in its 'order' value (which lists all parts to be displayed). Moreover, this implements 'get_threadline_structure': a method that will compile a dict with all threadline theming infos for a given thread object. --- alot/settings/theme.py | 59 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/alot/settings/theme.py b/alot/settings/theme.py index 1585cff9..c5f3f69a 100644 --- a/alot/settings/theme.py +++ b/alot/settings/theme.py @@ -2,11 +2,13 @@ # This file is released under the GNU GPL, version 3 or a later revision. # For further details see the COPYING file import os +import logging from urwid import AttrSpec, AttrSpecError from utils import read_config from checks import align_mode from checks import attr_triple +from checks import width_tuple from errors import ConfigError DEFAULTSPATH = os.path.join(os.path.dirname(__file__), '..', 'defaults') @@ -23,7 +25,23 @@ class Theme(object): self._spec = os.path.join(DEFAULTSPATH, 'theme.spec') self._config = read_config(path, self._spec, checks={'align': align_mode, + 'widthtuple': width_tuple, 'attrtriple': attr_triple}) + self._colours = [1, 16, 256] + # make sure every entry in 'order' lists have their own subsections + for sec in self._config['search']: + if sec.startswith('threadline'): + threadline = self._config['search'][sec] + if 'order' in threadline: + listed = set(threadline['order']) + present = set(threadline.sections) + difference = listed.difference(present) + if difference: + msg = 'missing threadline parts: %s' % difference + raise ConfigError(msg) + + def _by_colour(self, triple, colour): + return triple[self._colours.index(colour)] def get_attribute(self, mode, name, colourmode): """ @@ -36,4 +54,43 @@ class Theme(object): :param colourmode: colour mode; in [1, 16, 256] :type colourmode: int """ - return self._config[mode][name][[1, 16, 256].index(colourmode)] + return self._config[mode][name][self._colours.index(colourmode)] + + def get_threadline_structure(self, thread, colourmode): + def pickcolour(triple): + return triple[self._colours.index(colourmode)] + + def matches(sec, thread): + if 'tags_contain' in sec.scalars: + if not set(sec['tags_contain']).issubset(thread.get_tags()): + return False + if 'query' in sec.scalars: + if not thread.matches(sec['query']): + return False + return True + + default = self._config['search']['threadline'].copy() + match = default + + for candidatename in self._config['search'].sections: + candidate = self._config['search'][candidatename] + logging.debug('testing:%s' % candidatename) + if candidatename.startswith('threadline') and\ + matches(candidate, thread): + match = candidate + break + #logging.debug('match: %s' % match) + + # fill in values + res = {} + res['normal'] = pickcolour(match.get('normal', default['normal'])) + res['focus'] = pickcolour(match.get('focus', default['focus'])) + res['order'] = match.get('order', default['order']) + for part in res['order']: + res[part] = {} + res[part]['width'] = match[part].get('width', ('fit', 0, 0)) + res[part]['alignment'] = match[part].get('alignment') + res[part]['normal'] = pickcolour(match[part].get('normal', default['normal'])) + res[part]['focus'] = pickcolour(match[part].get('focus', default['focus'])) + logging.debug(res) + return res -- cgit v1.2.3 From c0d59d6c0ac9d1f65a9a9becbcbf8af8f1baf790 Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Sat, 7 Jul 2012 11:41:41 +0100 Subject: make sure width is set --- alot/settings/theme.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alot/settings/theme.py b/alot/settings/theme.py index c5f3f69a..2b708ce6 100644 --- a/alot/settings/theme.py +++ b/alot/settings/theme.py @@ -88,7 +88,7 @@ class Theme(object): res['order'] = match.get('order', default['order']) for part in res['order']: res[part] = {} - res[part]['width'] = match[part].get('width', ('fit', 0, 0)) + res[part]['width'] = match[part].get('width') or ('fit', 0, 0) res[part]['alignment'] = match[part].get('alignment') res[part]['normal'] = pickcolour(match[part].get('normal', default['normal'])) res[part]['focus'] = pickcolour(match[part].get('focus', default['focus'])) -- cgit v1.2.3 From 7bd9489ed8988eb66a40eaff6b60fb6d051bd88c Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Sat, 7 Jul 2012 11:59:31 +0100 Subject: rewrite Threadline widget This makes threadline build its parts according to a "threadline theming structure" it reads of a Theme object. This structure determines which subwidgets in which order are used and for each of them provides normal/focussed attributes, align and width --- alot/widgets.py | 221 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 130 insertions(+), 91 deletions(-) diff --git a/alot/widgets.py b/alot/widgets.py index e98fbf7b..5986c49a 100644 --- a/alot/widgets.py +++ b/alot/widgets.py @@ -92,75 +92,85 @@ class ThreadlineWidget(urwid.AttrMap): def __init__(self, tid, dbman): self.dbman = dbman - #logging.debug('tid: %s' % tid) self.thread = dbman.get_thread(tid) - #logging.debug('tid: %s' % self.thread) self.tag_widgets = [] self.display_content = settings.get('display_content_in_threadline') + self.structure = None self.rebuild() - normal = settings.get_theming_attribute('search', 'thread') - focussed = settings.get_theming_attribute('search', 'thread_focus') + normal = self.structure['normal'] + focussed = self.structure['focus'] urwid.AttrMap.__init__(self, self.columns, normal, focussed) - def rebuild(self): - cols = [] - if self.thread: - newest = self.thread.get_newest_date() - else: + def _build_part(self, name, struct, minw, maxw, align): + def pad(string, shorten=None): + if maxw: + if len(string) > maxw: + if shorten: + string = shorten(string, maxw) + else: + string = string[:maxw] + if minw: + if len(string) < minw: + if align == 'left': + string = string.ljust(minw) + elif align == 'center': + string = string.center(minw) + else: + string = string.rjust(minw) + return string + + part = None + width = None + if name == 'date': newest = None - if newest == None: datestring = '' - else: - datestring = settings.represent_datetime(newest) - datestring = datestring.rjust(self.pretty_datetime_len) - self.date_w = urwid.AttrMap(urwid.Text(datestring), - self._get_theme('date')) - cols.append(('fixed', len(datestring), self.date_w)) - - if self.thread: - mailcountstring = "(%d)" % self.thread.get_total_messages() - else: - mailcountstring = "(?)" - self.mailcount_w = urwid.AttrMap(urwid.Text(mailcountstring), - self._get_theme('mailcount')) - cols.append(('fixed', len(mailcountstring), self.mailcount_w)) - - if self.thread: - self.tag_widgets = [TagWidget(t) - for t in self.thread.get_tags()] - else: - self.tag_widgets = [] - self.tag_widgets.sort(tag_cmp, - lambda tag_widget: tag_widget.translated) - for tag_widget in self.tag_widgets: - if not tag_widget.hidden: - cols.append(('fixed', tag_widget.width(), tag_widget)) - - if self.thread: - authors = self.thread.get_authors_string() or '(None)' - else: - authors = '(None)' - maxlength = settings.get('authors_maxlength') - authorsstring = shorten_author_string(authors, maxlength) - self.authors_w = urwid.AttrMap(urwid.Text(authorsstring), - self._get_theme('authors')) - cols.append(('fixed', len(authorsstring), self.authors_w)) - - if self.thread: - subjectstring = self.thread.get_subject() or '' - else: - subjectstring = '' - # sanitize subject string: - subjectstring = subjectstring.replace('\n', ' ') - subjectstring = subjectstring.replace('\r', '') - subjectstring = subjectstring.strip() - - self.subject_w = urwid.AttrMap(urwid.Text(subjectstring, wrap='clip'), - self._get_theme('subject')) - if subjectstring: - cols.append(('weight', 2, self.subject_w)) - - if self.display_content: + if self.thread: + newest = self.thread.get_newest_date() + datestring = settings.represent_datetime(newest) + datestring = datestring.rjust(self.pretty_datetime_len) + datestring = pad(datestring) + width = len(datestring) + part = AttrFlipWidget(urwid.Text(datestring), struct['date']) + + elif name == 'mailcount': + if self.thread: + mailcountstring = "(%d)" % self.thread.get_total_messages() + else: + mailcountstring = "(?)" + datestring = pad(mailcountstring) + width = len(mailcountstring) + mailcount_w = AttrFlipWidget(urwid.Text(mailcountstring), + struct['mailcount']) + part = mailcount_w + elif name == 'authors': + if self.thread: + authors = self.thread.get_authors_string() or '(None)' + else: + authors = '(None)' + maxlength = settings.get('authors_maxlength') #TODO + authorsstring = pad(authors, shorten_author_string) + authors_w = AttrFlipWidget(urwid.Text(authorsstring), + struct['authors']) + width = len(authorsstring) + part = authors_w + + elif name == 'subject': + if self.thread: + subjectstring = self.thread.get_subject() or '' + else: + subjectstring = '' + # sanitize subject string: + subjectstring = subjectstring.replace('\n', ' ') + subjectstring = subjectstring.replace('\r', '') + subjectstring = pad(subjectstring.strip()) + + subject_w = AttrFlipWidget(urwid.Text(subjectstring, wrap='clip'), + struct['subject']) + if subjectstring: + width = len(subjectstring) + part = subject_w + + elif name == 'content': if self.thread: msgs = self.thread.get_messages().keys() else: @@ -168,39 +178,66 @@ class ThreadlineWidget(urwid.AttrMap): # sort the most recent messages first msgs.sort(key=lambda msg: msg.get_date(), reverse=True) lastcontent = ' '.join([m.get_text_content() for m in msgs]) - contentstring = lastcontent.replace('\n', ' ').strip() - self.content_w = urwid.AttrMap(urwid.Text( + contentstring = pad(lastcontent.replace('\n', ' ').strip()) + content_w = AttrFlipWidget(urwid.Text( contentstring, wrap='clip'), - self._get_theme('content')) - cols.append(self.content_w) + struct['content']) + width = len(contentstring) + part = content_w + elif name == 'tags': + if self.thread: + tag_widgets = [TagWidget(t) + for t in self.thread.get_tags()] + tag_widgets.sort(tag_cmp, + lambda tag_widget: tag_widget.translated) + else: + tag_widgets = [] + cols = [] + length = -1 + for tag_widget in tag_widgets: + if not tag_widget.hidden: + wrapped_tagwidget = AttrFlipWidget(tag_widget, struct['tags']) + tag_width = tag_widget.width() + cols.append(('fixed', tag_width, wrapped_tagwidget)) + length += tag_width +1 + if cols: + part = urwid.Columns(cols, dividechars=1) + width = length + return width, part - self.columns = urwid.Columns(cols, dividechars=1) + def rebuild(self): + self.widgets = [] + columns = [] + self.structure = settings.get_threadline_theming(self.thread) + for partname in self.structure['order']: + minw = maxw = None + width_tuple = self.structure[partname]['width'] + if width_tuple is not None: + if width_tuple[0] == 'fit': + minw, maxw = width_tuple[1:] + align_mode = self.structure[partname]['alignment'] + width, part = self._build_part(partname, self.structure, + minw, maxw, align_mode) + if part is not None: + if isinstance(part, urwid.Columns): + for w in part.widget_list: + self.widgets.append(w) + else: + self.widgets.append(part) + + # compute width and align + if width_tuple[0] == 'weight': + columnentry = width_tuple + (part,) + else: + columnentry = ('fixed', width, part) + columns.append(columnentry) + self.columns = urwid.Columns(columns, dividechars=1) self.original_widget = self.columns def render(self, size, focus=False): - if focus: - self.date_w.set_attr_map({None: self._get_theme('date', focus)}) - self.mailcount_w.set_attr_map({None: - self._get_theme('mailcount', focus)}) - for tw in self.tag_widgets: - tw.set_focussed() - self.authors_w.set_attr_map({None: self._get_theme('authors', - focus)}) - self.subject_w.set_attr_map({None: self._get_theme('subject', - focus)}) - if self.display_content: - self.content_w.set_attr_map( - {None: self._get_theme('content', focus=True)}) - else: - self.date_w.set_attr_map({None: self._get_theme('date')}) - self.mailcount_w.set_attr_map({None: self._get_theme('mailcount')}) - for tw in self.tag_widgets: - tw.set_unfocussed() - self.authors_w.set_attr_map({None: self._get_theme('authors')}) - self.subject_w.set_attr_map({None: self._get_theme('subject')}) - if self.display_content: - self.content_w.set_attr_map({None: self._get_theme('content')}) + for w in self.widgets: + w.set_map('focus' if focus else 'normal') return urwid.AttrMap.render(self, size, focus) def selectable(self): @@ -213,10 +250,12 @@ class ThreadlineWidget(urwid.AttrMap): return self.thread def _get_theme(self, component, focus=False): - attr_key = 'thread_{0}'.format(component) + path = ['search', 'threadline', component] if focus: - attr_key += '_focus' - return settings.get_theming_attribute('search', attr_key) + path.append('focus') + else: + path.append('normal') + return settings.get_theming_attribute(path) class BufferlineWidget(urwid.Text): -- cgit v1.2.3 From ff493ee46493b832d5e42c14372873c057332d40 Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Sat, 7 Jul 2012 12:01:48 +0100 Subject: use width tuples correctly in themes --- alot/defaults/default.theme | 4 +++- alot/defaults/theme.spec | 6 ++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/alot/defaults/default.theme b/alot/defaults/default.theme index e66b5c5d..1f87c5ec 100644 --- a/alot/defaults/default.theme +++ b/alot/defaults/default.theme @@ -41,7 +41,7 @@ [[[mailcount]]] normal = 'default','','light gray','default','light gray','default' focus = 'standout','','light gray','dark gray','g89','#68a' - width = 'fit',5,5 + width = 'fit', 5,5 [[[tags]]] normal = 'bold','','brown','default','#a86','default' focus = 'standout','','yellow,bold','dark gray','#ff8','#68a' @@ -52,6 +52,8 @@ [[[subject]]] normal = 'default','','light gray','default','g58','default' focus = 'standout','','light gray','dark gray','g89','#68a' + width = 'weight', 1 [[[content]]] normal = 'default','','dark gray','default','#866','default' focus = 'standout','','black','dark gray','#866','#68a' + width = 'weight', 1 diff --git a/alot/defaults/theme.spec b/alot/defaults/theme.spec index f3814aa5..825c2060 100644 --- a/alot/defaults/theme.spec +++ b/alot/defaults/theme.spec @@ -27,8 +27,7 @@ [[[__many__]]] normal = attrtriple focus = attrtriple - #width_fixed = integer - #width_weight = integer + width = widthtuple(default=None) alignment = align(default='right') [[__many__]] normal = attrtriple @@ -37,8 +36,7 @@ [[[__many__]]] normal = attrtriple focus = attrtriple - #width_fixed = integer - #width_weight = integer + width = widthtuple(default=None) alignment = align(default='right') [thread] attachment = attrtriple -- cgit v1.2.3 From 3c539e1c7b37f772d67f4eaf95c590030f91bcc1 Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Sat, 7 Jul 2012 12:02:33 +0100 Subject: fix with_tuple configobj check make sure it always returns a tuple --- alot/settings/checks.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/alot/settings/checks.py b/alot/settings/checks.py index eff1370e..ca412b68 100644 --- a/alot/settings/checks.py +++ b/alot/settings/checks.py @@ -62,19 +62,28 @@ def width_tuple(value): """ test if value is a valid width indicator (for a sub-widget in a column). This can either be - ('fit', min, max): use the length actually needed for the content, padded to - use at least width min, and cut of at width max. Here, min - and max are positive integers or 0 to disable the boundary. + ('fit', min, max): use the length actually needed for the content, padded + to use at least width min, and cut of at width max. + Here, min and max are positive integers or 0 to disable + the boundary. ('weight',n): have it relative weight of n compared to other columns. Here, n is an int. """ if value is None: - value = 'fit',0,0 + res = 'fit', 0, 0 elif not isinstance(value, (list, tuple)): raise VdtTypeError(value) elif value[0] not in ['fit', 'weight']: raise VdtTypeError(value) - return value + if value[0] == 'fit': + if not isinstance(value[1], int) or not isinstance(value[2], int): + VdtTypeError(value) + res = 'fit', int(value[1]), int(value[2]) + else: + if not isinstance(value[1], int): + VdtTypeError(value) + res = 'weight', int(value[1]) + return res def mail_container(value): -- cgit v1.2.3 From eb9ed7fc7c7802af1e58d47c0df7c0afa0a3726d Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Sat, 7 Jul 2012 12:47:49 +0100 Subject: rename threadline order --> parts --- alot/defaults/default.theme | 2 +- alot/defaults/theme.spec | 4 ++-- alot/settings/theme.py | 11 ++++++----- alot/widgets.py | 2 +- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/alot/defaults/default.theme b/alot/defaults/default.theme index 1f87c5ec..00ea84eb 100644 --- a/alot/defaults/default.theme +++ b/alot/defaults/default.theme @@ -33,7 +33,7 @@ [[threadline]] normal = 'default','','default','default','#6d6','default' focus = 'standout','','light gray','dark gray','white','#68a' - order = date,mailcount,tags,authors,subject + parts = date,mailcount,tags,authors,subject [[[date]]] normal = 'default','','light gray','default','g58','default' focus = 'standout','','light gray','dark gray','g89','#68a' diff --git a/alot/defaults/theme.spec b/alot/defaults/theme.spec index 825c2060..3f8d1108 100644 --- a/alot/defaults/theme.spec +++ b/alot/defaults/theme.spec @@ -23,7 +23,7 @@ focus = attrtriple # order subwidgets are displayed. subset of {date,mailcount,tags,authors,subject,count} # every element listed must have its own subsection below - order = string_list(default=list(date,mailcount,tags,authors,subject)) + parts = string_list(default=None) [[[__many__]]] normal = attrtriple focus = attrtriple @@ -32,7 +32,7 @@ [[__many__]] normal = attrtriple focus = attrtriple - order = string_list(default=list(date,mailcount,tags,authors,subject)) + parts = string_list(default=None) [[[__many__]]] normal = attrtriple focus = attrtriple diff --git a/alot/settings/theme.py b/alot/settings/theme.py index 2b708ce6..b3a47b1b 100644 --- a/alot/settings/theme.py +++ b/alot/settings/theme.py @@ -31,9 +31,11 @@ class Theme(object): # make sure every entry in 'order' lists have their own subsections for sec in self._config['search']: if sec.startswith('threadline'): + logging.debug(sec) threadline = self._config['search'][sec] - if 'order' in threadline: - listed = set(threadline['order']) + logging.debug(threadline) + if threadline['parts'] is not None: + listed = set(threadline['parts']) present = set(threadline.sections) difference = listed.difference(present) if difference: @@ -79,14 +81,13 @@ class Theme(object): matches(candidate, thread): match = candidate break - #logging.debug('match: %s' % match) # fill in values res = {} res['normal'] = pickcolour(match.get('normal', default['normal'])) res['focus'] = pickcolour(match.get('focus', default['focus'])) - res['order'] = match.get('order', default['order']) - for part in res['order']: + res['parts'] = match.get('parts', default['parts']) + for part in res['parts']: res[part] = {} res[part]['width'] = match[part].get('width') or ('fit', 0, 0) res[part]['alignment'] = match[part].get('alignment') diff --git a/alot/widgets.py b/alot/widgets.py index 5986c49a..3ddd1903 100644 --- a/alot/widgets.py +++ b/alot/widgets.py @@ -210,7 +210,7 @@ class ThreadlineWidget(urwid.AttrMap): self.widgets = [] columns = [] self.structure = settings.get_threadline_theming(self.thread) - for partname in self.structure['order']: + for partname in self.structure['parts']: minw = maxw = None width_tuple = self.structure[partname]['width'] if width_tuple is not None: -- cgit v1.2.3 From a6f2b93ea477f13d96eaf40fb4dd7cb7b9c97efc Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Sat, 7 Jul 2012 12:49:46 +0100 Subject: update converter --- extra/theme_convert.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/extra/theme_convert.py b/extra/theme_convert.py index 45446bed..b9317b04 100755 --- a/extra/theme_convert.py +++ b/extra/theme_convert.py @@ -18,9 +18,12 @@ def get_leaf_value(cfg, path, fallback=''): else: return cfg[path[0]] else: - scfg = cfg[path[0]] - sp = path[1:] - return get_leaf_value(scfg, sp, fallback) + if path[0] in cfg: + scfg = cfg[path[0]] + sp = path[1:] + return get_leaf_value(scfg, sp, fallback) + else: + return None if __name__ == "__main__": @@ -37,14 +40,14 @@ if __name__ == "__main__": def lookup(path): values = [] for c in ['1', '16', '256']: - values.append(get_leaf_value(old, [c] + path + ['fg'])) - values.append(get_leaf_value(old, [c] + path + ['bg'])) + values.append(get_leaf_value(old, [c] + path + ['fg']) or 'default') + values.append(get_leaf_value(old, [c] + path + ['bg']) or 'default') values = map(lambda s: '\'' + s + '\'', values) return ','.join(values) for bmode in ['global', 'help', 'bufferlist', 'thread', 'envelope']: out.write('[%s]\n' % bmode) - for themable in old['1'][bmode].sections: + for themable in old['16'][bmode].sections: out.write(' %s = %s\n' % (themable, lookup([bmode, themable]))) out.write('[search]\n') @@ -52,7 +55,7 @@ if __name__ == "__main__": out.write(' ' * 8 + 'normal = %s\n' % lookup(['search', 'thread'])) out.write(' ' * 8 + 'focus = %s\n' % lookup(['search', 'thread_focus'])) - out.write(' ' * 8 + 'order = date,mailcount,tags,authors,subject\n') + out.write(' ' * 8 + 'parts = date,mailcount,tags,authors,subject\n') out.write(' ' * 8 + '[[[date]]]\n') out.write(' ' * 12 + 'normal = %s\n' % lookup(['search', 'thread_date'])) -- cgit v1.2.3 From 5ebb57ca4f408d1d098caa9fac2c012f824b8621 Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Sat, 7 Jul 2012 13:52:42 +0100 Subject: implement selective threadline theming aka. 'highlighting' This allows the user to specify alternate 'threadline.*' sections for searchmode: They specify the difference to the default threadline theming. Each of those can contain fields 'query' and 'tagged_with' to determine if they apply: We go through all those threadline sections top down, for each of them check if the conditions query (thread matches querystring) and 'tagged_with' (culmulative tags of messages in thread contain those listed). The first section that matches wins, default is to section 'threadline'. --- alot/defaults/theme.spec | 10 ++++++---- alot/settings/theme.py | 50 ++++++++++++++++++++++++++---------------------- 2 files changed, 33 insertions(+), 27 deletions(-) diff --git a/alot/defaults/theme.spec b/alot/defaults/theme.spec index 3f8d1108..f5ce8341 100644 --- a/alot/defaults/theme.spec +++ b/alot/defaults/theme.spec @@ -30,12 +30,14 @@ width = widthtuple(default=None) alignment = align(default='right') [[__many__]] - normal = attrtriple - focus = attrtriple + normal = attrtriple(default=None) + focus = attrtriple(default=None) parts = string_list(default=None) + query = string(default=None) + tagged_with = force_list(default=None) [[[__many__]]] - normal = attrtriple - focus = attrtriple + normal = attrtriple(default=None) + focus = attrtriple(default=None) width = widthtuple(default=None) alignment = align(default='right') [thread] diff --git a/alot/settings/theme.py b/alot/settings/theme.py index b3a47b1b..8b60b2f1 100644 --- a/alot/settings/theme.py +++ b/alot/settings/theme.py @@ -2,13 +2,13 @@ # This file is released under the GNU GPL, version 3 or a later revision. # For further details see the COPYING file import os -import logging from urwid import AttrSpec, AttrSpecError from utils import read_config from checks import align_mode from checks import attr_triple from checks import width_tuple +from checks import force_list from errors import ConfigError DEFAULTSPATH = os.path.join(os.path.dirname(__file__), '..', 'defaults') @@ -26,19 +26,20 @@ class Theme(object): self._config = read_config(path, self._spec, checks={'align': align_mode, 'widthtuple': width_tuple, + 'force_list': force_list, 'attrtriple': 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'): - logging.debug(sec) - threadline = self._config['search'][sec] - logging.debug(threadline) - if threadline['parts'] is not None: - listed = set(threadline['parts']) - present = set(threadline.sections) - difference = listed.difference(present) - if difference: + 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' % difference raise ConfigError(msg) @@ -63,35 +64,38 @@ class Theme(object): return triple[self._colours.index(colourmode)] def matches(sec, thread): - if 'tags_contain' in sec.scalars: - if not set(sec['tags_contain']).issubset(thread.get_tags()): + if sec.get('tagged_with') is not None: + if not set(sec['tagged_with']).issubset(thread.get_tags()): return False - if 'query' in sec.scalars: + if sec.get('query') is not None: if not thread.matches(sec['query']): return False return True - default = self._config['search']['threadline'].copy() + default = self._config['search']['threadline'] match = default - for candidatename in self._config['search'].sections: + candidates = self._config['search'].sections + for candidatename in candidates: candidate = self._config['search'][candidatename] - logging.debug('testing:%s' % 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', default['normal'])) - res['focus'] = pickcolour(match.get('focus', default['focus'])) - res['parts'] = match.get('parts', default['parts']) + 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']: + partsec = match.get(part) or default[part] res[part] = {} - res[part]['width'] = match[part].get('width') or ('fit', 0, 0) - res[part]['alignment'] = match[part].get('alignment') - res[part]['normal'] = pickcolour(match[part].get('normal', default['normal'])) - res[part]['focus'] = pickcolour(match[part].get('focus', default['focus'])) - logging.debug(res) + res[part]['width'] = partsec.get('width') or ('fit', 0, 0) + res[part]['alignment'] = partsec.get('alignment') + normal_triple = partsec.get('normal') or default['normal'] + res[part]['normal'] = pickcolour(normal_triple) + focus_triple = partsec.get('focus') or default['focus'] + res[part]['focus'] = pickcolour(focus_triple) return res -- cgit v1.2.3 From dba31fba12b00dd1ff4a9a38aa61f49d044e4d7f Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Sat, 7 Jul 2012 14:45:54 +0100 Subject: highlight unread threads in default theme --- alot/defaults/default.theme | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/alot/defaults/default.theme b/alot/defaults/default.theme index 00ea84eb..84c18608 100644 --- a/alot/defaults/default.theme +++ b/alot/defaults/default.theme @@ -57,3 +57,20 @@ normal = 'default','','dark gray','default','#866','default' focus = 'standout','','black','dark gray','#866','#68a' width = 'weight', 1 + + # highlight threads containing unread messages + [[threadline-unread]] + normal = 'default','','default,bold','default','#6d6,bold','default' + tagged_with = 'unread' + [[[date]]] + normal = 'default','','light gray,bold','default','g58,bold','default' + [[[mailcount]]] + normal = 'default','','light gray,bold','default','light gray,bold','default' + [[[tags]]] + normal = 'bold','','brown,bold','default','#a86,bold','default' + [[[authors]]] + normal = 'default,underline','','dark green,bold','default','#6d6,bold','default' + [[[subject]]] + normal = 'default','','light gray,bold','default','g58,bold','default' + [[[content]]] + normal = 'default','','dark gray,bold','default','#866,bold','default' -- cgit v1.2.3 From 9db841c0d22c1f7a035b5942a6a858910949e1dc Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Sat, 7 Jul 2012 14:48:04 +0100 Subject: add urwids colour picker --- extra/palette_test.py | 252 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 252 insertions(+) create mode 100755 extra/palette_test.py diff --git a/extra/palette_test.py b/extra/palette_test.py new file mode 100755 index 00000000..271dd517 --- /dev/null +++ b/extra/palette_test.py @@ -0,0 +1,252 @@ +#!/usr/bin/python +# +# Urwid Palette Test. Showing off highcolor support +# Copyright (C) 2004-2009 Ian Ward +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Urwid web site: http://excess.org/urwid/ + +""" +Palette test. Shows the available foreground and background settings +in monochrome, 16 color, 88 color and 256 color modes. +""" + +import re +import sys + +import urwid +import urwid.raw_display + +CHART_256 = """ +brown__ dark_red_ dark_magenta_ dark_blue_ dark_cyan_ dark_green_ +yellow_ light_red light_magenta light_blue light_cyan light_green + + #00f#06f#08f#0af#0df#0ff black_______ dark_gray___ + #60f#00d#06d#08d#0ad#0dd#0fd light_gray__ white_______ + #80f#60d#00a#06a#08a#0aa#0da#0fa + #a0f#80d#60a#008#068#088#0a8#0d8#0f8 + #d0f#a0d#80d#608#006#066#086#0a6#0d6#0f6 + #f0f#d0d#a0a#808#606#000#060#080#0a0#0d0#0f0#0f6#0f8#0fa#0fd#0ff + #f0d#d0a#a08#806#600#660#680#6a0#6d0#6f0#6f6#6f8#6fa#6fd#6ff#0df + #f0a#d08#a06#800#860#880#8a0#8d0#8f0#8f6#8f8#8fa#8fd#8ff#6df#0af + #f08#d06#a00#a60#a80#aa0#ad0#af0#af6#af8#afa#afd#aff#8df#6af#08f + #f06#d00#d60#d80#da0#dd0#df0#df6#df8#dfa#dfd#dff#adf#8af#68f#06f + #f00#f60#f80#fa0#fd0#ff0#ff6#ff8#ffa#ffd#fff#ddf#aaf#88f#66f#00f + #fd0#fd6#fd8#fda#fdd#fdf#daf#a8f#86f#60f + #66d#68d#6ad#6dd #fa0#fa6#fa8#faa#fad#faf#d8f#a6f#80f + #86d#66a#68a#6aa#6da #f80#f86#f88#f8a#f8d#f8f#d6f#a0f + #a6d#86a#668#688#6a8#6d8 #f60#f66#f68#f6a#f6d#f6f#d0f +#d6d#a6a#868#666#686#6a6#6d6#6d8#6da#6dd #f00#f06#f08#f0a#f0d#f0f + #d6a#a68#866#886#8a6#8d6#8d8#8da#8dd#6ad + #d68#a66#a86#aa6#ad6#ad8#ada#add#8ad#68d + #d66#d86#da6#dd6#dd8#dda#ddd#aad#88d#66d g78_g82_g85_g89_g93_g100 + #da6#da8#daa#dad#a8d#86d g52_g58_g62_g66_g70_g74_ + #88a#8aa #d86#d88#d8a#d8d#a6d g27_g31_g35_g38_g42_g46_g50_ + #a8a#888#8a8#8aa #d66#d68#d6a#d6d g0__g3__g7__g11_g15_g19_g23_ + #a88#aa8#aaa#88a + #a88#a8a +""" + +CHART_88 = """ +brown__ dark_red_ dark_magenta_ dark_blue_ dark_cyan_ dark_green_ +yellow_ light_red light_magenta light_blue light_cyan light_green + + #00f#08f#0cf#0ff black_______ dark_gray___ + #80f#00c#08c#0cc#0fc light_gray__ white_______ + #c0f#80c#008#088#0c8#0f8 +#f0f#c0c#808#000#080#0c0#0f0#0f8#0fc#0ff #88c#8cc + #f0c#c08#800#880#8c0#8f0#8f8#8fc#8ff#0cf #c8c#888#8c8#8cc + #f08#c00#c80#cc0#cf0#cf8#cfc#cff#8cf#08f #c88#cc8#ccc#88c + #f00#f80#fc0#ff0#ff8#ffc#fff#ccf#88f#00f #c88#c8c + #fc0#fc8#fcc#fcf#c8f#80f + #f80#f88#f8c#f8f#c0f g62_g74_g82_g89_g100 + #f00#f08#f0c#f0f g0__g19_g35_g46_g52 +""" + +CHART_16 = """ +brown__ dark_red_ dark_magenta_ dark_blue_ dark_cyan_ dark_green_ +yellow_ light_red light_magenta light_blue light_cyan light_green + +black_______ dark_gray___ light_gray__ white_______ +""" + +ATTR_RE = re.compile("(?P[ \n]*)(?P[^ \n]+)") +SHORT_ATTR = 4 # length of short high-colour descriptions which may +# be packed one after the next + +def parse_chart(chart, convert): + """ + Convert string chart into text markup with the correct attributes. + + chart -- palette chart as a string + convert -- function that converts a single palette entry to an + (attr, text) tuple, or None if no match is found + """ + out = [] + for match in re.finditer(ATTR_RE, chart): + if match.group('whitespace'): + out.append(match.group('whitespace')) + entry = match.group('entry') + entry = entry.replace("_", " ") + while entry: + # try the first four characters + attrtext = convert(entry[:SHORT_ATTR]) + if attrtext: + elen = SHORT_ATTR + entry = entry[SHORT_ATTR:].strip() + else: # try the whole thing + attrtext = convert(entry.strip()) + assert attrtext, "Invalid palette entry: %r" % entry + elen = len(entry) + entry = "" + attr, text = attrtext + out.append((attr, text.ljust(elen))) + return out + +def foreground_chart(chart, background, colors): + """ + Create text markup for a foreground colour chart + + chart -- palette chart as string + background -- colour to use for background of chart + colors -- number of colors (88 or 256) + """ + def convert_foreground(entry): + try: + attr = urwid.AttrSpec(entry, background, colors) + except urwid.AttrSpecError: + return None + return attr, entry + return parse_chart(chart, convert_foreground) + +def background_chart(chart, foreground, colors): + """ + Create text markup for a background colour chart + + chart -- palette chart as string + foreground -- colour to use for foreground of chart + colors -- number of colors (88 or 256) + + This will remap 8 <= colour < 16 to high-colour versions + in the hopes of greater compatibility + """ + def convert_background(entry): + try: + attr = urwid.AttrSpec(foreground, entry, colors) + except urwid.AttrSpecError: + return None + # fix 8 <= colour < 16 + if colors > 16 and attr.background_basic and \ + attr.background_number >= 8: + # use high-colour with same number + entry = 'h%d'%attr.background_number + attr = urwid.AttrSpec(foreground, entry, colors) + return attr, entry + return parse_chart(chart, convert_background) + + +def main(): + palette = [ + ('header', 'black,underline', 'light gray', 'standout,underline', + 'black,underline', '#88a'), + ('panel', 'light gray', 'dark blue', '', + '#ffd', '#00a'), + ('focus', 'light gray', 'dark cyan', 'standout', + '#ff8', '#806'), + ] + + screen = urwid.raw_display.Screen() + screen.register_palette(palette) + + lb = urwid.SimpleListWalker([]) + chart_offset = None # offset of chart in lb list + + mode_radio_buttons = [] + chart_radio_buttons = [] + + def fcs(widget): + # wrap widgets that can take focus + return urwid.AttrMap(widget, None, 'focus') + + def set_mode(colors, is_foreground_chart): + # set terminal mode and redraw chart + screen.set_terminal_properties(colors) + screen.reset_default_terminal_palette() + + chart_fn = (background_chart, foreground_chart)[is_foreground_chart] + if colors == 1: + lb[chart_offset] = urwid.Divider() + else: + chart = {16: CHART_16, 88: CHART_88, 256: CHART_256}[colors] + txt = chart_fn(chart, 'default', colors) + lb[chart_offset] = urwid.Text(txt, wrap='clip') + + def on_mode_change(rb, state, colors): + # if this radio button is checked + if state: + is_foreground_chart = chart_radio_buttons[0].state + set_mode(colors, is_foreground_chart) + + def mode_rb(text, colors, state=False): + # mode radio buttons + rb = urwid.RadioButton(mode_radio_buttons, text, state) + urwid.connect_signal(rb, 'change', on_mode_change, colors) + return fcs(rb) + + def on_chart_change(rb, state): + # handle foreground check box state change + set_mode(screen.colors, state) + + def click_exit(button): + raise urwid.ExitMainLoop() + + lb.extend([ + urwid.AttrMap(urwid.Text("Urwid Palette Test"), 'header'), + urwid.AttrMap(urwid.Columns([ + urwid.Pile([ + mode_rb("Monochrome", 1), + mode_rb("16-Color", 16, True), + mode_rb("88-Color", 88), + mode_rb("256-Color", 256),]), + urwid.Pile([ + fcs(urwid.RadioButton(chart_radio_buttons, + "Foreground Colors", True, on_chart_change)), + fcs(urwid.RadioButton(chart_radio_buttons, + "Background Colors")), + urwid.Divider(), + fcs(urwid.Button("Exit", click_exit)), + ]), + ]),'panel') + ]) + + chart_offset = len(lb) + lb.extend([ + urwid.Divider() # placeholder for the chart + ]) + + set_mode(16, True) # displays the chart + + def unhandled_input(key): + if key in ('Q','q','esc'): + raise urwid.ExitMainLoop() + + urwid.MainLoop(urwid.ListBox(lb), screen=screen, + unhandled_input=unhandled_input).run() + +if __name__ == "__main__": + main() + + -- cgit v1.2.3 From defeba9036c511dc1641dd5ba4c8dde16c90f78c Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Sat, 7 Jul 2012 14:53:35 +0100 Subject: extra: colour_picker this modifies urwids palette_test so that it shows attricutes as alot would render them in your terminal. See comment inline. --- extra/colour_picker.py | 259 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 259 insertions(+) create mode 100755 extra/colour_picker.py diff --git a/extra/colour_picker.py b/extra/colour_picker.py new file mode 100755 index 00000000..17fcf449 --- /dev/null +++ b/extra/colour_picker.py @@ -0,0 +1,259 @@ +#!/usr/bin/python +# +# COLOUR PICKER. +# This is a lightly modified version of urwids palette_test.py example script as +# found at https://raw.github.com/wardi/urwid/master/examples/palette_test.py +# +# This version simply omits resetting the screens default colour palette, +# and therefore displays the colour attributes as alot would render them in +# your terminal. +# +# Urwid Palette Test. Showing off highcolor support +# Copyright (C) 2004-2009 Ian Ward +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Urwid web site: http://excess.org/urwid/ + +""" +Palette test. Shows the available foreground and background settings +in monochrome, 16 color, 88 color and 256 color modes. +""" + +import re +import sys + +import urwid +import urwid.raw_display + +CHART_256 = """ +brown__ dark_red_ dark_magenta_ dark_blue_ dark_cyan_ dark_green_ +yellow_ light_red light_magenta light_blue light_cyan light_green + + #00f#06f#08f#0af#0df#0ff black_______ dark_gray___ + #60f#00d#06d#08d#0ad#0dd#0fd light_gray__ white_______ + #80f#60d#00a#06a#08a#0aa#0da#0fa + #a0f#80d#60a#008#068#088#0a8#0d8#0f8 + #d0f#a0d#80d#608#006#066#086#0a6#0d6#0f6 + #f0f#d0d#a0a#808#606#000#060#080#0a0#0d0#0f0#0f6#0f8#0fa#0fd#0ff + #f0d#d0a#a08#806#600#660#680#6a0#6d0#6f0#6f6#6f8#6fa#6fd#6ff#0df + #f0a#d08#a06#800#860#880#8a0#8d0#8f0#8f6#8f8#8fa#8fd#8ff#6df#0af + #f08#d06#a00#a60#a80#aa0#ad0#af0#af6#af8#afa#afd#aff#8df#6af#08f + #f06#d00#d60#d80#da0#dd0#df0#df6#df8#dfa#dfd#dff#adf#8af#68f#06f + #f00#f60#f80#fa0#fd0#ff0#ff6#ff8#ffa#ffd#fff#ddf#aaf#88f#66f#00f + #fd0#fd6#fd8#fda#fdd#fdf#daf#a8f#86f#60f + #66d#68d#6ad#6dd #fa0#fa6#fa8#faa#fad#faf#d8f#a6f#80f + #86d#66a#68a#6aa#6da #f80#f86#f88#f8a#f8d#f8f#d6f#a0f + #a6d#86a#668#688#6a8#6d8 #f60#f66#f68#f6a#f6d#f6f#d0f +#d6d#a6a#868#666#686#6a6#6d6#6d8#6da#6dd #f00#f06#f08#f0a#f0d#f0f + #d6a#a68#866#886#8a6#8d6#8d8#8da#8dd#6ad + #d68#a66#a86#aa6#ad6#ad8#ada#add#8ad#68d + #d66#d86#da6#dd6#dd8#dda#ddd#aad#88d#66d g78_g82_g85_g89_g93_g100 + #da6#da8#daa#dad#a8d#86d g52_g58_g62_g66_g70_g74_ + #88a#8aa #d86#d88#d8a#d8d#a6d g27_g31_g35_g38_g42_g46_g50_ + #a8a#888#8a8#8aa #d66#d68#d6a#d6d g0__g3__g7__g11_g15_g19_g23_ + #a88#aa8#aaa#88a + #a88#a8a +""" + +CHART_88 = """ +brown__ dark_red_ dark_magenta_ dark_blue_ dark_cyan_ dark_green_ +yellow_ light_red light_magenta light_blue light_cyan light_green + + #00f#08f#0cf#0ff black_______ dark_gray___ + #80f#00c#08c#0cc#0fc light_gray__ white_______ + #c0f#80c#008#088#0c8#0f8 +#f0f#c0c#808#000#080#0c0#0f0#0f8#0fc#0ff #88c#8cc + #f0c#c08#800#880#8c0#8f0#8f8#8fc#8ff#0cf #c8c#888#8c8#8cc + #f08#c00#c80#cc0#cf0#cf8#cfc#cff#8cf#08f #c88#cc8#ccc#88c + #f00#f80#fc0#ff0#ff8#ffc#fff#ccf#88f#00f #c88#c8c + #fc0#fc8#fcc#fcf#c8f#80f + #f80#f88#f8c#f8f#c0f g62_g74_g82_g89_g100 + #f00#f08#f0c#f0f g0__g19_g35_g46_g52 +""" + +CHART_16 = """ +brown__ dark_red_ dark_magenta_ dark_blue_ dark_cyan_ dark_green_ +yellow_ light_red light_magenta light_blue light_cyan light_green + +black_______ dark_gray___ light_gray__ white_______ +""" + +ATTR_RE = re.compile("(?P[ \n]*)(?P[^ \n]+)") +SHORT_ATTR = 4 # length of short high-colour descriptions which may +# be packed one after the next + +def parse_chart(chart, convert): + """ + Convert string chart into text markup with the correct attributes. + + chart -- palette chart as a string + convert -- function that converts a single palette entry to an + (attr, text) tuple, or None if no match is found + """ + out = [] + for match in re.finditer(ATTR_RE, chart): + if match.group('whitespace'): + out.append(match.group('whitespace')) + entry = match.group('entry') + entry = entry.replace("_", " ") + while entry: + # try the first four characters + attrtext = convert(entry[:SHORT_ATTR]) + if attrtext: + elen = SHORT_ATTR + entry = entry[SHORT_ATTR:].strip() + else: # try the whole thing + attrtext = convert(entry.strip()) + assert attrtext, "Invalid palette entry: %r" % entry + elen = len(entry) + entry = "" + attr, text = attrtext + out.append((attr, text.ljust(elen))) + return out + +def foreground_chart(chart, background, colors): + """ + Create text markup for a foreground colour chart + + chart -- palette chart as string + background -- colour to use for background of chart + colors -- number of colors (88 or 256) + """ + def convert_foreground(entry): + try: + attr = urwid.AttrSpec(entry, background, colors) + except urwid.AttrSpecError: + return None + return attr, entry + return parse_chart(chart, convert_foreground) + +def background_chart(chart, foreground, colors): + """ + Create text markup for a background colour chart + + chart -- palette chart as string + foreground -- colour to use for foreground of chart + colors -- number of colors (88 or 256) + + This will remap 8 <= colour < 16 to high-colour versions + in the hopes of greater compatibility + """ + def convert_background(entry): + try: + attr = urwid.AttrSpec(foreground, entry, colors) + except urwid.AttrSpecError: + return None + # fix 8 <= colour < 16 + if colors > 16 and attr.background_basic and \ + attr.background_number >= 8: + # use high-colour with same number + entry = 'h%d'%attr.background_number + attr = urwid.AttrSpec(foreground, entry, colors) + return attr, entry + return parse_chart(chart, convert_background) + + +def main(): + palette = [ + ('header', 'black,underline', 'light gray', 'standout,underline', + 'black,underline', '#88a'), + ('panel', 'light gray', 'dark blue', '', + '#ffd', '#00a'), + ('focus', 'light gray', 'dark cyan', 'standout', + '#ff8', '#806'), + ] + + screen = urwid.raw_display.Screen() + screen.register_palette(palette) + + lb = urwid.SimpleListWalker([]) + chart_offset = None # offset of chart in lb list + + mode_radio_buttons = [] + chart_radio_buttons = [] + + def fcs(widget): + # wrap widgets that can take focus + return urwid.AttrMap(widget, None, 'focus') + + def set_mode(colors, is_foreground_chart): + # set terminal mode and redraw chart + screen.set_terminal_properties(colors) + + chart_fn = (background_chart, foreground_chart)[is_foreground_chart] + if colors == 1: + lb[chart_offset] = urwid.Divider() + else: + chart = {16: CHART_16, 88: CHART_88, 256: CHART_256}[colors] + txt = chart_fn(chart, 'default', colors) + lb[chart_offset] = urwid.Text(txt, wrap='clip') + + def on_mode_change(rb, state, colors): + # if this radio button is checked + if state: + is_foreground_chart = chart_radio_buttons[0].state + set_mode(colors, is_foreground_chart) + + def mode_rb(text, colors, state=False): + # mode radio buttons + rb = urwid.RadioButton(mode_radio_buttons, text, state) + urwid.connect_signal(rb, 'change', on_mode_change, colors) + return fcs(rb) + + def on_chart_change(rb, state): + # handle foreground check box state change + set_mode(screen.colors, state) + + def click_exit(button): + raise urwid.ExitMainLoop() + + lb.extend([ + urwid.AttrMap(urwid.Text("Urwid Palette Test"), 'header'), + urwid.AttrMap(urwid.Columns([ + urwid.Pile([ + mode_rb("Monochrome", 1), + mode_rb("16-Color", 16, True), + mode_rb("88-Color", 88), + mode_rb("256-Color", 256),]), + urwid.Pile([ + fcs(urwid.RadioButton(chart_radio_buttons, + "Foreground Colors", True, on_chart_change)), + fcs(urwid.RadioButton(chart_radio_buttons, + "Background Colors")), + urwid.Divider(), + fcs(urwid.Button("Exit", click_exit)), + ]), + ]),'panel') + ]) + + chart_offset = len(lb) + lb.extend([ + urwid.Divider() # placeholder for the chart + ]) + + set_mode(16, True) # displays the chart + + def unhandled_input(key): + if key in ('Q','q','esc'): + raise urwid.ExitMainLoop() + + urwid.MainLoop(urwid.ListBox(lb), screen=screen, + unhandled_input=unhandled_input).run() + +if __name__ == "__main__": + main() + + -- cgit v1.2.3 From 396902f9042483c0c1aa8c4f89f369e570942ae3 Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Sat, 7 Jul 2012 18:44:03 +0100 Subject: add solarized_dark theme to extras --- extra/themes/solarized_dark | 119 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 extra/themes/solarized_dark diff --git a/extra/themes/solarized_dark b/extra/themes/solarized_dark new file mode 100644 index 00000000..3aec302c --- /dev/null +++ b/extra/themes/solarized_dark @@ -0,0 +1,119 @@ +############################################################################### +# SOLARIZED DARK +# +# colour theme for alot. © 2012 Patrick Totzke, GNU GPL3+ +# http://ethanschoonover.com/solarized +# https://github.com/pazz/alot +############################################################################### +# +# Define mappings from solarized colour names to urwid attribute names for 16 +# and 256 colour modes. These work well assuming you use the solarized term +# colours via Xressources/Xdefaults. You might want to change this otherwise + +16_base03 = 'dark gray' +16_base02 = 'black' +16_base01 = 'light green' +16_base00 = 'yellow' +16_base0 = 'light blue' +16_base1 = 'light cyan' +16_base2 = 'light gray' +16_base3 = 'white' +16_yellow = 'brown' +16_orange = 'light red' +16_red = 'dark red' +16_magenta = 'dark magenta' +16_violet = 'light magenta' +16_blue = 'dark blue' +16_cyan = 'dark cyan' +16_green = 'dark green' + +# Use a slightly different mapping here to be able to use "bold" in 256c mode +256_base03 = 'dark gray' +256_base02 = 'black' +256_base01 = 'light green' +256_base00 = 'yellow' +256_base0 = 'g66' +256_base1 = 'g70' +256_base2 = 'light gray' +256_base3 = 'white' +256_yellow = 'brown' +256_orange = 'light red' +256_red = 'dark red' +256_magenta = 'dark magenta' +256_violet = 'light magenta' +256_blue = 'dark blue' +256_cyan = '#088' +256_green = 'dark green' + + +# This is the actual alot theme +[global] + footer = 'standout','default','%(16_base0)s','%(16_base03)s','%(256_base0)s','%(256_base03)s' + body = 'default','default','%(16_base0)s','%(16_base03)s','%(256_base0)s','%(256_base03)s' + notify_error = 'standout','default','%(16_base03)s','%(16_red)s','%(256_base03)s','%(256_red)s' + notify_normal = 'default','default','%(16_base00)s','%(16_base03)s','%(256_base00)s','%(256_base03)s' + prompt = 'default','default','%(16_base0)s','%(16_base02)s','%(256_base0)s','%(256_base02)s' + tag = 'default','default','%(16_yellow)s','%(16_base03)s','%(256_yellow)s','%(256_base03)s' + tag_focus = 'default','default','%(16_base02)s','%(16_yellow)s','%(256_base02)s','%(256_yellow)s' +[help] + text = 'default','default','light gray','dark gray','%(256_base0)s','%(256_base03)s' + section = 'underline','default','%(16_base1)s','%(16_base02)s','%(256_base1)s','%(256_base02)s' + title = 'standout','default','%(16_base1)s','%(16_base03)s','%(256_base1)s','%(256_base03)s' +[bufferlist] + focus = 'standout','default','%(16_base02)s','%(16_yellow)s','%(256_base02)s','%(256_yellow)s' + results_even = 'default','default','%(16_base0)s','%(16_base03)s','%(256_base0)s','%(256_base03)s' + results_odd = 'default','default','%(16_base0)s','%(16_base02)s','%(256_base0)s','%(256_base02)s' +[thread] + attachment = 'default','default','%(16_base0)s','%(16_base03)s','%(256_base0)s','%(256_base03)s' + attachment_focus = 'underline','default','%(16_base02)s','%(16_yellow)s','%(256_base02)s','%(256_yellow)s' + body = 'default','default','%(16_base0)s','%(16_base03)s','%(256_base0)s','%(256_base03)s' + header = 'default','default','%(16_base0)s','%(16_base02)s','%(256_base0)s','%(256_base02)s' + header_key = 'default','default','%(16_magenta)s','%(16_base02)s','%(256_magenta)s','%(256_base02)s' + header_value = 'default','default','%(16_blue)s','%(16_base02)s','%(256_blue)s','%(256_base02)s' + summary_even = 'default','default','%(16_base0)s','%(16_base02)s','%(256_base0)s','%(256_base02)s' + summary_focus = 'standout','default','%(16_base02)s','%(16_yellow)s','%(256_base02)s','%(256_yellow)s' + summary_odd = 'default','default','%(16_base0)s','%(16_base03)s','%(256_base0)s','%(256_base03)s' +[envelope] + body = 'default','default','%(16_base0)s','%(16_base03)s','%(256_base0)s','%(256_base03)s' + header = 'default','default','%(16_base0)s','%(16_base02)s','%(256_base0)s','%(256_base02)s' + header_key = 'default','default','%(16_orange)s','%(16_base02)s','%(256_orange)s','%(256_base02)s' + header_value = 'default','default','%(16_violet)s','%(16_base02)s','%(256_violet)s','%(256_base02)s' +[search] + [[threadline]] + normal = 'default','default','%(16_base1)s','%(16_base03)s','%(256_base1)s','%(256_base03)s' + focus = 'standout','default','%(16_base02)s','%(16_yellow)s','%(256_base02)s','%(256_yellow)s' + parts = date,mailcount,tags,authors,subject + [[[date]]] + normal = 'default','default','%(16_base1)s','%(16_base03)s','%(256_base1)s','%(256_base03)s' + focus = 'standout','default','%(16_base02)s','%(16_yellow)s','%(256_base02)s','%(256_yellow)s' + [[[mailcount]]] + normal = 'default','default','%(16_base1)s','%(16_base03)s','%(256_base1)s','%(256_base03)s' + focus = 'standout','default','%(16_base01)s','%(16_yellow)s','%(256_base01)s','%(256_yellow)s' + [[[tags]]] + normal = 'bold','default','%(16_yellow)s','%(16_base03)s','%(256_yellow)s','%(256_base03)s' + focus = 'standout','default','%(16_base02)s','%(16_orange)s','%(256_base02)s','%(256_orange)s' + [[[authors]]] + normal = 'default,underline','default','%(16_cyan)s','%(16_base03)s','%(256_cyan)s','%(256_base03)s' + focus = 'standout','default','%(16_base02)s,bold','%(16_yellow)s','%(256_base02)s,bold','%(256_yellow)s' + [[[subject]]] + normal = 'default','default','%(16_base0)s','%(16_base03)s','%(256_base0)s','%(256_base03)s' + focus = 'standout','default','%(16_base01)s','%(16_yellow)s','%(256_base01)s','%(256_yellow)s' + width = 'weight',1 + [[[content]]] + normal = 'default','default','%(16_base01)s','%(16_base03)s','%(256_base01)s','%(256_base03)s' + focus = 'standout','default','%(16_base02)s','%(16_yellow)s','%(256_base02)s','%(256_yellow)s' + [[threadline-unread]] + normal = 'default','default','%(16_base1)s,bold','%(16_base03)s','%(256_base1)s,bold','%(256_base03)s' + tagged_with = 'unread' + [[[date]]] + normal = 'default','default','%(16_base1)s,bold','%(16_base03)s','%(256_base1)s,bold','%(256_base03)s' + [[[mailcount]]] + normal = 'default','default','%(16_base1)s,bold','%(16_base03)s','%(256_base1)s,bold','%(256_base03)s' + [[[tags]]] + normal = 'bold','default','%(16_yellow)s,bold','%(16_base03)s','%(256_yellow)s,bold','%(256_base03)s' + [[[authors]]] + normal = 'default,underline','default','%(16_cyan)s','%(16_base03)s','%(256_cyan)s,bold','%(256_base03)s' + [[[subject]]] + normal = 'default','default','%(16_base0)s,bold','%(16_base03)s','%(256_base0)s,bold','%(256_base03)s' + [[[content]]] + normal = 'default','default','%(16_base01)s,bold','%(16_base03)s','%(256_base01)s,bold','%(256_base03)s' -- cgit v1.2.3 From 2c87edaeb924b4f4089ff753bd5f6a04a6a537a0 Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Sun, 8 Jul 2012 17:14:43 +0100 Subject: update solarized dark theme background for notifications --- extra/themes/solarized_dark | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extra/themes/solarized_dark b/extra/themes/solarized_dark index 3aec302c..351b2045 100644 --- a/extra/themes/solarized_dark +++ b/extra/themes/solarized_dark @@ -50,8 +50,8 @@ [global] footer = 'standout','default','%(16_base0)s','%(16_base03)s','%(256_base0)s','%(256_base03)s' body = 'default','default','%(16_base0)s','%(16_base03)s','%(256_base0)s','%(256_base03)s' - notify_error = 'standout','default','%(16_base03)s','%(16_red)s','%(256_base03)s','%(256_red)s' - notify_normal = 'default','default','%(16_base00)s','%(16_base03)s','%(256_base00)s','%(256_base03)s' + notify_error = 'standout','default','%(16_base3)s','%(16_red)s','%(256_base3)s','%(256_red)s' + notify_normal = 'default','default','%(16_base01)s','%(16_base02)s','%(256_base01)s','%(256_base02)s' prompt = 'default','default','%(16_base0)s','%(16_base02)s','%(256_base0)s','%(256_base02)s' tag = 'default','default','%(16_yellow)s','%(16_base03)s','%(256_yellow)s','%(256_base03)s' tag_focus = 'default','default','%(16_base02)s','%(16_yellow)s','%(256_base02)s','%(256_yellow)s' -- cgit v1.2.3 From df685d48331bdf8081aff6be3b6f551bf54f06a1 Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Sun, 8 Jul 2012 17:15:27 +0100 Subject: add bright solarized theme --- extra/themes/solarized | 119 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 extra/themes/solarized diff --git a/extra/themes/solarized b/extra/themes/solarized new file mode 100644 index 00000000..7be7d1c6 --- /dev/null +++ b/extra/themes/solarized @@ -0,0 +1,119 @@ +############################################################################### +# SOLARIZED +# +# colour theme for alot. © 2012 Patrick Totzke, GNU GPL3+ +# http://ethanschoonover.com/solarized +# https://github.com/pazz/alot +############################################################################### +# +# Define mappings from solarized colour names to urwid attribute names for 16 +# and 256 colour modes. These work well assuming you use the solarized term +# colours via Xressources/Xdefaults. You might want to change this otherwise + +16_base03 = 'dark gray' +16_base02 = 'black' +16_base01 = 'light green' +16_base00 = 'yellow' +16_base0 = 'light blue' +16_base1 = 'light cyan' +16_base2 = 'light gray' +16_base3 = 'white' +16_yellow = 'brown' +16_orange = 'light red' +16_red = 'dark red' +16_magenta = 'dark magenta' +16_violet = 'light magenta' +16_blue = 'dark blue' +16_cyan = 'dark cyan' +16_green = 'dark green' + +# Use a slightly different mapping here to be able to use "bold" in 256c mode +256_base03 = 'dark gray' +256_base02 = 'black' +256_base01 = 'light green' +256_base00 = 'yellow' +256_base0 = 'g66' +256_base1 = 'g70' +256_base2 = 'light gray' +256_base3 = 'white' +256_yellow = 'brown' +256_orange = 'light red' +256_red = 'dark red' +256_magenta = 'dark magenta' +256_violet = 'light magenta' +256_blue = 'dark blue' +256_cyan = '#088' +256_green = 'dark green' + + +# This is the actual alot theme +[global] + footer = 'standout','default','%(16_base00)s','%(16_base3)s','%(256_base00)s','%(256_base3)s' + body = 'default','default','%(16_base00)s','%(16_base3)s','%(256_base00)s','%(256_base3)s' + notify_error = 'standout','default','%(16_base3)s','%(16_red)s','%(256_base3)s','%(256_red)s' + notify_normal = 'default','default','%(16_base00)s','%(16_base2)s','%(256_base00)s','%(256_base2)s' + prompt = 'default','default','%(16_base00)s','%(16_base2)s','%(256_base00)s','%(256_base2)s' + tag = 'default','default','%(16_yellow)s','%(16_base3)s','%(256_yellow)s','%(256_base3)s' + tag_focus = 'default','default','%(16_base2)s','%(16_yellow)s','%(256_base2)s','%(256_yellow)s' +[help] + text = 'default','default','light gray','dark gray','%(256_base00)s','%(256_base3)s' + section = 'underline','default','%(16_base01)s','%(16_base2)s','%(256_base01)s','%(256_base2)s' + title = 'standout','default','%(16_base01)s','%(16_base3)s','%(256_base01)s','%(256_base3)s' +[bufferlist] + focus = 'standout','default','%(16_base2)s','%(16_yellow)s','%(256_base2)s','%(256_yellow)s' + results_even = 'default','default','%(16_base00)s','%(16_base3)s','%(256_base00)s','%(256_base3)s' + results_odd = 'default','default','%(16_base00)s','%(16_base2)s','%(256_base00)s','%(256_base2)s' +[thread] + attachment = 'default','default','%(16_base00)s','%(16_base3)s','%(256_base00)s','%(256_base3)s' + attachment_focus = 'underline','default','%(16_base2)s','%(16_yellow)s','%(256_base2)s','%(256_yellow)s' + body = 'default','default','%(16_base00)s','%(16_base3)s','%(256_base00)s','%(256_base3)s' + header = 'default','default','%(16_base00)s','%(16_base2)s','%(256_base00)s','%(256_base2)s' + header_key = 'default','default','%(16_magenta)s','%(16_base2)s','%(256_magenta)s','%(256_base2)s' + header_value = 'default','default','%(16_blue)s','%(16_base2)s','%(256_blue)s','%(256_base2)s' + summary_even = 'default','default','%(16_base00)s','%(16_base2)s','%(256_base00)s','%(256_base2)s' + summary_focus = 'standout','default','%(16_base2)s','%(16_yellow)s','%(256_base2)s','%(256_yellow)s' + summary_odd = 'default','default','%(16_base00)s','%(16_base3)s','%(256_base00)s','%(256_base3)s' +[envelope] + body = 'default','default','%(16_base00)s','%(16_base3)s','%(256_base00)s','%(256_base3)s' + header = 'default','default','%(16_base00)s','%(16_base2)s','%(256_base00)s','%(256_base2)s' + header_key = 'default','default','%(16_orange)s','%(16_base2)s','%(256_orange)s','%(256_base2)s' + header_value = 'default','default','%(16_violet)s','%(16_base2)s','%(256_violet)s','%(256_base2)s' +[search] + [[threadline]] + normal = 'default','default','%(16_base01)s','%(16_base3)s','%(256_base01)s','%(256_base3)s' + focus = 'standout','default','%(16_base2)s','%(16_yellow)s','%(256_base2)s','%(256_yellow)s' + parts = date,mailcount,tags,authors,subject + [[[date]]] + normal = 'default','default','%(16_base01)s','%(16_base3)s','%(256_base01)s','%(256_base3)s' + focus = 'standout','default','%(16_base2)s','%(16_yellow)s','%(256_base2)s','%(256_yellow)s' + [[[mailcount]]] + normal = 'default','default','%(16_base01)s','%(16_base3)s','%(256_base01)s','%(256_base3)s' + focus = 'standout','default','%(16_base1)s','%(16_yellow)s','%(256_base1)s','%(256_yellow)s' + [[[tags]]] + normal = 'bold','default','%(16_yellow)s','%(16_base3)s','%(256_yellow)s','%(256_base3)s' + focus = 'standout','default','%(16_base2)s','%(16_orange)s','%(256_base2)s','%(256_orange)s' + [[[authors]]] + normal = 'default,underline','default','%(16_cyan)s','%(16_base3)s','%(256_cyan)s','%(256_base3)s' + focus = 'standout','default','%(16_base2)s,bold','%(16_yellow)s','%(256_base2)s,bold','%(256_yellow)s' + [[[subject]]] + normal = 'default','default','%(16_base00)s','%(16_base3)s','%(256_base00)s','%(256_base3)s' + focus = 'standout','default','%(16_base1)s','%(16_yellow)s','%(256_base1)s','%(256_yellow)s' + width = 'weight',1 + [[[content]]] + normal = 'default','default','%(16_base1)s','%(16_base3)s','%(256_base1)s','%(256_base3)s' + focus = 'standout','default','%(16_base2)s','%(16_yellow)s','%(256_base2)s','%(256_yellow)s' + [[threadline-unread]] + normal = 'default','default','%(16_base01)s,bold','%(16_base3)s','%(256_base01)s,bold','%(256_base3)s' + tagged_with = 'unread' + [[[date]]] + normal = 'default','default','%(16_base01)s,bold','%(16_base3)s','%(256_base01)s,bold','%(256_base3)s' + [[[mailcount]]] + normal = 'default','default','%(16_base01)s,bold','%(16_base3)s','%(256_base01)s,bold','%(256_base3)s' + [[[tags]]] + normal = 'bold','default','%(16_yellow)s,bold','%(16_base3)s','%(256_yellow)s,bold','%(256_base3)s' + [[[authors]]] + normal = 'default,underline','default','%(16_cyan)s','%(16_base3)s','%(256_cyan)s,bold','%(256_base3)s' + [[[subject]]] + normal = 'default','default','%(16_base00)s,bold','%(16_base3)s','%(256_base00)s,bold','%(256_base3)s' + [[[content]]] + normal = 'default','default','%(16_base1)s,bold','%(16_base3)s','%(256_base1)s,bold','%(256_base3)s' -- cgit v1.2.3 From 01923d94ea499136214f4a4ecdb53352817ff625 Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Sun, 8 Jul 2012 19:57:28 +0100 Subject: change tags formatting in config spec the individual tag subsections define 'normal' and 'focus' attributes to define how the tag is represented if unfocussed and focussed respectively. These are urwid attribute sextuples that define fg/bg for 1,16 and 256 coour modes --- alot/defaults/alot.rc.spec | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/alot/defaults/alot.rc.spec b/alot/defaults/alot.rc.spec index cd5dd6c3..ed6c1a99 100644 --- a/alot/defaults/alot.rc.spec +++ b/alot/defaults/alot.rc.spec @@ -179,14 +179,10 @@ forward_subject_prefix = string(default='Fwd: ') [tags] # for each tag [[__many__]] - # foreground - fg = string(default=None) - # background - bg = string(default=None) - # foreground if focused - focus_fg = string(default=None) - # background if focused - focus_bg = string(default=None) + # unfocussed + normal = attrtriple(default=None) + # focussed + focus = attrtriple(default=None) # don't display at all? hidden = boolean(default=False) # alternative string representation -- cgit v1.2.3 From 36d4eea8648d764406e920f54913da5e29bf77ca Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Sun, 8 Jul 2012 20:00:07 +0100 Subject: compile tagstring representation according to the given attribudes read in the new format --- alot/settings/__init__.py | 62 ++++++++++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/alot/settings/__init__.py b/alot/settings/__init__.py index 130feb8d..c367d49c 100644 --- a/alot/settings/__init__.py +++ b/alot/settings/__init__.py @@ -233,7 +233,7 @@ class SettingsManager(object): colours = int(self._config.get('colourmode')) return self._theme.get_threadline_structure(thread, colours) - def get_tagstring_representation(self, tag): + def get_tagstring_representation(self, tag, normal=None, focus=None): """ looks up user's preferred way to represent a given tagstring @@ -241,41 +241,47 @@ class SettingsManager(object): 'normal' and 'focussed' to `urwid.AttrSpec` sttributes, and 'translated' to an alternative string representation """ - colours = int(self._config.get('colourmode')) - # default attributes: normal and focussed - default = self._theme.get_attribute('global', 'tag', colours) - default_f = self._theme.get_attribute('global', 'tag_focus', colours) - for sec in self._config['tags'].sections: + colourmode = int(self._config.get('colourmode')) + theme = self._theme + cfg = self._config + colours = [1, 16, 256] + + def resolve_att(triple, fallback): + if triple is None: + return fallback + a = triple[colours.index(colourmode)] + if a.background in ['default', '']: + a.background = fallback.background + if a.foreground in ['default', '']: + a.foreground = fallfore.foreground + return a + + default_normal = theme.get_attribute('global', 'tag', colourmode) + default_focus = theme.get_attribute('global', 'tag_focus', colourmode) + fallback_normal = normal or default_normal + fallback_focus = focus or default_focus + + for sec in cfg['tags'].sections: if re.match('^' + sec + '$', tag): - fg = self._config['tags'][sec]['fg'] or default.foreground - bg = self._config['tags'][sec]['bg'] or default.background - try: - normal = urwid.AttrSpec(fg, bg, colours) - except AttrSpecError: - normal = default - focus_fg = self._config['tags'][sec]['focus_fg'] - focus_fg = focus_fg or default_f.foreground - focus_bg = self._config['tags'][sec]['focus_bg'] - focus_bg = focus_bg or default_f.background - try: - focussed = urwid.AttrSpec(focus_fg, focus_bg, colours) - except AttrSpecError: - focussed = default_f - - hidden = self._config['tags'][sec]['hidden'] or False - - translated = self._config['tags'][sec]['translated'] or tag - translation = self._config['tags'][sec]['translation'] + normal = resolve_att(cfg['tags'][sec]['normal'], + fallback_normal) + logging.debug(normal) + focus = resolve_att(cfg['tags'][sec]['focus'], fallback_focus) + + hidden = cfg['tags'][sec]['hidden'] or False + + translated = cfg['tags'][sec]['translated'] or tag + translation = cfg['tags'][sec]['translation'] if translation: translated = re.sub(translation[0], translation[1], tag) break else: - normal = default - focussed = default_f + normal = fallback_normal + focus = fallback_focus hidden = False translated = tag - return {'normal': normal, 'focussed': focussed, + return {'normal': normal, 'focussed': focus, 'hidden': hidden, 'translated': translated} def get_hook(self, key): -- cgit v1.2.3 From da8d74d0031390fad514871404257dd3d561ea89 Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Sun, 8 Jul 2012 20:01:21 +0100 Subject: proper tagstring representation in ThreadlineWidgets in search mode. These now work in all colourmodes and moreover simulate cascading attributes so that if fg/bg is undefined in the tagstring representation, the attribute from the widget *below* is used instead of a global default --- alot/widgets.py | 36 +++++--- extra/palette_test.py | 252 -------------------------------------------------- 2 files changed, 22 insertions(+), 266 deletions(-) delete mode 100755 extra/palette_test.py diff --git a/alot/widgets.py b/alot/widgets.py index 3ddd1903..eab132ac 100644 --- a/alot/widgets.py +++ b/alot/widgets.py @@ -147,7 +147,7 @@ class ThreadlineWidget(urwid.AttrMap): authors = self.thread.get_authors_string() or '(None)' else: authors = '(None)' - maxlength = settings.get('authors_maxlength') #TODO + maxlength = settings.get('authors_maxlength') # TODO authorsstring = pad(authors, shorten_author_string) authors_w = AttrFlipWidget(urwid.Text(authorsstring), struct['authors']) @@ -187,7 +187,9 @@ class ThreadlineWidget(urwid.AttrMap): part = content_w elif name == 'tags': if self.thread: - tag_widgets = [TagWidget(t) + fallback_normal = self.structure['normal'] + fallback_focus = self.structure['focus'] + tag_widgets = [TagWidget(t, fallback_normal, fallback_focus) for t in self.thread.get_tags()] tag_widgets.sort(tag_cmp, lambda tag_widget: tag_widget.translated) @@ -197,10 +199,10 @@ class ThreadlineWidget(urwid.AttrMap): length = -1 for tag_widget in tag_widgets: if not tag_widget.hidden: - wrapped_tagwidget = AttrFlipWidget(tag_widget, struct['tags']) + wrapped_tagwidget = tag_widget tag_width = tag_widget.width() cols.append(('fixed', tag_width, wrapped_tagwidget)) - length += tag_width +1 + length += tag_width + 1 if cols: part = urwid.Columns(cols, dividechars=1) width = length @@ -288,15 +290,21 @@ class TagWidget(urwid.AttrMap): tag may also be configured as hidden, which users of this widget should honour. """ - def __init__(self, tag): + def __init__(self, tag, fallback_normal=None, fallback_focus=None): self.tag = tag - representation = settings.get_tagstring_representation(tag) + representation = settings.get_tagstring_representation(tag, + fallback_normal, + fallback_focus) self.hidden = representation['hidden'] self.translated = representation['translated'] self.txt = urwid.Text(self.translated, wrap='clip') - self.normal_att = representation['normal'] - self.focus_att = representation['focussed'] - urwid.AttrMap.__init__(self, self.txt, self.normal_att, self.focus_att) + normal_att = representation['normal'] + focus_att = representation['focussed'] + self.attmaps = {'normal': normal_att, 'focus': focus_att} + urwid.AttrMap.__init__(self, self.txt, normal_att) + + def set_map(self, attrstring): + self.set_attr_map({None: self.attmaps[attrstring]}) def width(self): # evil voodoo hotfix for double width chars that may @@ -313,10 +321,10 @@ class TagWidget(urwid.AttrMap): return self.tag def set_focussed(self): - self.set_attr_map({None: self.focus_att}) + self.set_attr_map(self.attmap['focus']) def set_unfocussed(self): - self.set_attr_map({None: self.normal_att}) + self.set_attr_map(self.attmap['normal']) class ChoiceWidget(urwid.Text): @@ -526,10 +534,10 @@ class MessageWidget(urwid.WidgetWrap): lines = [] for key in self._displayed_headers: if key in mail: - if key.lower() in ['cc','bcc', 'to']: + if key.lower() in ['cc', 'bcc', 'to']: values = mail.get_all(key) - dvalues = [decode_header(v, normalize=norm) for v in values] - lines.append((key, ', '.join(dvalues))) + values = [decode_header(v, normalize=norm) for v in values] + lines.append((key, ', '.join(values))) else: for value in mail.get_all(key): dvalue = decode_header(value, normalize=norm) diff --git a/extra/palette_test.py b/extra/palette_test.py deleted file mode 100755 index 271dd517..00000000 --- a/extra/palette_test.py +++ /dev/null @@ -1,252 +0,0 @@ -#!/usr/bin/python -# -# Urwid Palette Test. Showing off highcolor support -# Copyright (C) 2004-2009 Ian Ward -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library 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 -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -# -# Urwid web site: http://excess.org/urwid/ - -""" -Palette test. Shows the available foreground and background settings -in monochrome, 16 color, 88 color and 256 color modes. -""" - -import re -import sys - -import urwid -import urwid.raw_display - -CHART_256 = """ -brown__ dark_red_ dark_magenta_ dark_blue_ dark_cyan_ dark_green_ -yellow_ light_red light_magenta light_blue light_cyan light_green - - #00f#06f#08f#0af#0df#0ff black_______ dark_gray___ - #60f#00d#06d#08d#0ad#0dd#0fd light_gray__ white_______ - #80f#60d#00a#06a#08a#0aa#0da#0fa - #a0f#80d#60a#008#068#088#0a8#0d8#0f8 - #d0f#a0d#80d#608#006#066#086#0a6#0d6#0f6 - #f0f#d0d#a0a#808#606#000#060#080#0a0#0d0#0f0#0f6#0f8#0fa#0fd#0ff - #f0d#d0a#a08#806#600#660#680#6a0#6d0#6f0#6f6#6f8#6fa#6fd#6ff#0df - #f0a#d08#a06#800#860#880#8a0#8d0#8f0#8f6#8f8#8fa#8fd#8ff#6df#0af - #f08#d06#a00#a60#a80#aa0#ad0#af0#af6#af8#afa#afd#aff#8df#6af#08f - #f06#d00#d60#d80#da0#dd0#df0#df6#df8#dfa#dfd#dff#adf#8af#68f#06f - #f00#f60#f80#fa0#fd0#ff0#ff6#ff8#ffa#ffd#fff#ddf#aaf#88f#66f#00f - #fd0#fd6#fd8#fda#fdd#fdf#daf#a8f#86f#60f - #66d#68d#6ad#6dd #fa0#fa6#fa8#faa#fad#faf#d8f#a6f#80f - #86d#66a#68a#6aa#6da #f80#f86#f88#f8a#f8d#f8f#d6f#a0f - #a6d#86a#668#688#6a8#6d8 #f60#f66#f68#f6a#f6d#f6f#d0f -#d6d#a6a#868#666#686#6a6#6d6#6d8#6da#6dd #f00#f06#f08#f0a#f0d#f0f - #d6a#a68#866#886#8a6#8d6#8d8#8da#8dd#6ad - #d68#a66#a86#aa6#ad6#ad8#ada#add#8ad#68d - #d66#d86#da6#dd6#dd8#dda#ddd#aad#88d#66d g78_g82_g85_g89_g93_g100 - #da6#da8#daa#dad#a8d#86d g52_g58_g62_g66_g70_g74_ - #88a#8aa #d86#d88#d8a#d8d#a6d g27_g31_g35_g38_g42_g46_g50_ - #a8a#888#8a8#8aa #d66#d68#d6a#d6d g0__g3__g7__g11_g15_g19_g23_ - #a88#aa8#aaa#88a - #a88#a8a -""" - -CHART_88 = """ -brown__ dark_red_ dark_magenta_ dark_blue_ dark_cyan_ dark_green_ -yellow_ light_red light_magenta light_blue light_cyan light_green - - #00f#08f#0cf#0ff black_______ dark_gray___ - #80f#00c#08c#0cc#0fc light_gray__ white_______ - #c0f#80c#008#088#0c8#0f8 -#f0f#c0c#808#000#080#0c0#0f0#0f8#0fc#0ff #88c#8cc - #f0c#c08#800#880#8c0#8f0#8f8#8fc#8ff#0cf #c8c#888#8c8#8cc - #f08#c00#c80#cc0#cf0#cf8#cfc#cff#8cf#08f #c88#cc8#ccc#88c - #f00#f80#fc0#ff0#ff8#ffc#fff#ccf#88f#00f #c88#c8c - #fc0#fc8#fcc#fcf#c8f#80f - #f80#f88#f8c#f8f#c0f g62_g74_g82_g89_g100 - #f00#f08#f0c#f0f g0__g19_g35_g46_g52 -""" - -CHART_16 = """ -brown__ dark_red_ dark_magenta_ dark_blue_ dark_cyan_ dark_green_ -yellow_ light_red light_magenta light_blue light_cyan light_green - -black_______ dark_gray___ light_gray__ white_______ -""" - -ATTR_RE = re.compile("(?P[ \n]*)(?P[^ \n]+)") -SHORT_ATTR = 4 # length of short high-colour descriptions which may -# be packed one after the next - -def parse_chart(chart, convert): - """ - Convert string chart into text markup with the correct attributes. - - chart -- palette chart as a string - convert -- function that converts a single palette entry to an - (attr, text) tuple, or None if no match is found - """ - out = [] - for match in re.finditer(ATTR_RE, chart): - if match.group('whitespace'): - out.append(match.group('whitespace')) - entry = match.group('entry') - entry = entry.replace("_", " ") - while entry: - # try the first four characters - attrtext = convert(entry[:SHORT_ATTR]) - if attrtext: - elen = SHORT_ATTR - entry = entry[SHORT_ATTR:].strip() - else: # try the whole thing - attrtext = convert(entry.strip()) - assert attrtext, "Invalid palette entry: %r" % entry - elen = len(entry) - entry = "" - attr, text = attrtext - out.append((attr, text.ljust(elen))) - return out - -def foreground_chart(chart, background, colors): - """ - Create text markup for a foreground colour chart - - chart -- palette chart as string - background -- colour to use for background of chart - colors -- number of colors (88 or 256) - """ - def convert_foreground(entry): - try: - attr = urwid.AttrSpec(entry, background, colors) - except urwid.AttrSpecError: - return None - return attr, entry - return parse_chart(chart, convert_foreground) - -def background_chart(chart, foreground, colors): - """ - Create text markup for a background colour chart - - chart -- palette chart as string - foreground -- colour to use for foreground of chart - colors -- number of colors (88 or 256) - - This will remap 8 <= colour < 16 to high-colour versions - in the hopes of greater compatibility - """ - def convert_background(entry): - try: - attr = urwid.AttrSpec(foreground, entry, colors) - except urwid.AttrSpecError: - return None - # fix 8 <= colour < 16 - if colors > 16 and attr.background_basic and \ - attr.background_number >= 8: - # use high-colour with same number - entry = 'h%d'%attr.background_number - attr = urwid.AttrSpec(foreground, entry, colors) - return attr, entry - return parse_chart(chart, convert_background) - - -def main(): - palette = [ - ('header', 'black,underline', 'light gray', 'standout,underline', - 'black,underline', '#88a'), - ('panel', 'light gray', 'dark blue', '', - '#ffd', '#00a'), - ('focus', 'light gray', 'dark cyan', 'standout', - '#ff8', '#806'), - ] - - screen = urwid.raw_display.Screen() - screen.register_palette(palette) - - lb = urwid.SimpleListWalker([]) - chart_offset = None # offset of chart in lb list - - mode_radio_buttons = [] - chart_radio_buttons = [] - - def fcs(widget): - # wrap widgets that can take focus - return urwid.AttrMap(widget, None, 'focus') - - def set_mode(colors, is_foreground_chart): - # set terminal mode and redraw chart - screen.set_terminal_properties(colors) - screen.reset_default_terminal_palette() - - chart_fn = (background_chart, foreground_chart)[is_foreground_chart] - if colors == 1: - lb[chart_offset] = urwid.Divider() - else: - chart = {16: CHART_16, 88: CHART_88, 256: CHART_256}[colors] - txt = chart_fn(chart, 'default', colors) - lb[chart_offset] = urwid.Text(txt, wrap='clip') - - def on_mode_change(rb, state, colors): - # if this radio button is checked - if state: - is_foreground_chart = chart_radio_buttons[0].state - set_mode(colors, is_foreground_chart) - - def mode_rb(text, colors, state=False): - # mode radio buttons - rb = urwid.RadioButton(mode_radio_buttons, text, state) - urwid.connect_signal(rb, 'change', on_mode_change, colors) - return fcs(rb) - - def on_chart_change(rb, state): - # handle foreground check box state change - set_mode(screen.colors, state) - - def click_exit(button): - raise urwid.ExitMainLoop() - - lb.extend([ - urwid.AttrMap(urwid.Text("Urwid Palette Test"), 'header'), - urwid.AttrMap(urwid.Columns([ - urwid.Pile([ - mode_rb("Monochrome", 1), - mode_rb("16-Color", 16, True), - mode_rb("88-Color", 88), - mode_rb("256-Color", 256),]), - urwid.Pile([ - fcs(urwid.RadioButton(chart_radio_buttons, - "Foreground Colors", True, on_chart_change)), - fcs(urwid.RadioButton(chart_radio_buttons, - "Background Colors")), - urwid.Divider(), - fcs(urwid.Button("Exit", click_exit)), - ]), - ]),'panel') - ]) - - chart_offset = len(lb) - lb.extend([ - urwid.Divider() # placeholder for the chart - ]) - - set_mode(16, True) # displays the chart - - def unhandled_input(key): - if key in ('Q','q','esc'): - raise urwid.ExitMainLoop() - - urwid.MainLoop(urwid.ListBox(lb), screen=screen, - unhandled_input=unhandled_input).run() - -if __name__ == "__main__": - main() - - -- cgit v1.2.3 From 60b01c615bf9d42ad298fb8d542c7efe38fb818d Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Sun, 8 Jul 2012 20:06:42 +0100 Subject: remove depricated 'authors_maxlength' setting This can now be done in theme files by changing the width-value of the authors threadline part. For example: ''' [search] [[threadline]] [[[authors]]] width = 'fit', 0, 30 ''' --- alot/defaults/alot.rc.spec | 3 --- alot/widgets.py | 2 -- docs/source/configuration/alotrc_table.rst | 10 ---------- 3 files changed, 15 deletions(-) diff --git a/alot/defaults/alot.rc.spec b/alot/defaults/alot.rc.spec index ed6c1a99..174413fd 100644 --- a/alot/defaults/alot.rc.spec +++ b/alot/defaults/alot.rc.spec @@ -128,9 +128,6 @@ envelope_statusbar = mixed_list(string, string, default=list('[{buffer_no}: enve # timestamp format in `strftime format syntax `_ timestamp_format = string(default=None) -# maximal length of authors string in search mode before it gets truncated -authors_maxlength = integer(default=30) - # how to print messages: # this specifies a shell command used for printing. # threads/messages are piped to this command as plain text. diff --git a/alot/widgets.py b/alot/widgets.py index eab132ac..a939464b 100644 --- a/alot/widgets.py +++ b/alot/widgets.py @@ -77,7 +77,6 @@ class ThreadlineWidget(urwid.AttrMap): Respected settings: * `general.display_content_in_threadline` * `general.timestamp_format` - * `general.authors_maxlength` Theme settings: * `search_thread, search_thread_focus` * `search_thread_date, search_thread_date_focus` @@ -147,7 +146,6 @@ class ThreadlineWidget(urwid.AttrMap): authors = self.thread.get_authors_string() or '(None)' else: authors = '(None)' - maxlength = settings.get('authors_maxlength') # TODO authorsstring = pad(authors, shorten_author_string) authors_w = AttrFlipWidget(urwid.Text(authorsstring), struct['authors']) diff --git a/docs/source/configuration/alotrc_table.rst b/docs/source/configuration/alotrc_table.rst index e8d32966..5ba16d12 100644 --- a/docs/source/configuration/alotrc_table.rst +++ b/docs/source/configuration/alotrc_table.rst @@ -26,16 +26,6 @@ :default: "~" -.. _authors-maxlength: - -.. describe:: authors_maxlength - - maximal length of authors string in search mode before it gets truncated - - :type: integer - :default: 30 - - .. _bufferclose-focus-offset: .. describe:: bufferclose_focus_offset -- cgit v1.2.3 From b96cc4890e92442c6a2bd1f9faca89da92dcd368 Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Sun, 8 Jul 2012 21:24:20 +0100 Subject: correct typo --- alot/settings/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alot/settings/__init__.py b/alot/settings/__init__.py index c367d49c..8097fae4 100644 --- a/alot/settings/__init__.py +++ b/alot/settings/__init__.py @@ -253,7 +253,7 @@ class SettingsManager(object): if a.background in ['default', '']: a.background = fallback.background if a.foreground in ['default', '']: - a.foreground = fallfore.foreground + a.foreground = fallback.foreground return a default_normal = theme.get_attribute('global', 'tag', colourmode) -- cgit v1.2.3 From 3e88853016b4d9234b67c4a8a665e08e601d3b7e Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Sun, 8 Jul 2012 21:28:58 +0100 Subject: extra: add tagsections_convert.py a script that converts 'tags' config subsections that determine the format tagstrings are represented to the new format. --- extra/tagsections_convert.py | 63 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100755 extra/tagsections_convert.py diff --git a/extra/tagsections_convert.py b/extra/tagsections_convert.py new file mode 100755 index 00000000..4dbf4554 --- /dev/null +++ b/extra/tagsections_convert.py @@ -0,0 +1,63 @@ +#!/usr/bin/python + +from configobj import ConfigObj +import argparse +import sys +import re + + +def get_leaf_value(cfg, path, fallback=''): + if len(path) == 1: + if isinstance(cfg, ConfigObj): + if path[0] not in cfg.scalars: + return fallback + else: + return cfg[path[0]] + else: + if path[0] not in cfg: + return fallback + else: + return cfg[path[0]] + else: + if path[0] in cfg: + scfg = cfg[path[0]] + sp = path[1:] + return get_leaf_value(scfg, sp, fallback) + else: + return None + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description='update alot theme files') + parser.add_argument('configfile', type=argparse.FileType('r'), + help='theme file to convert') + parser.add_argument('-o', type=argparse.FileType('w'), dest='out', + help='destination', default=sys.stdout) + args = parser.parse_args() + + cfg = ConfigObj(args.configfile) + out = args.out + print args + + def is_256(att): + r = r'(g\d{1,3}(?!\d))|(#[0-9A-Fa-f]{3}(?![0-9A-Fa-f]))' + return re.search(r, att) + + if 'tags' in cfg: + for tag in cfg['tags'].sections: + sec = cfg['tags'][tag] + att = [''] * 6 + if 'fg' in sec: + fg = sec['fg'] + if not is_256(fg): + att[2] = fg + att[4] = fg + del(sec['fg']) + if 'bg' in sec: + bg = sec['bg'] + if not is_256(bg): + att[3] = bg + att[5] = bg + del(sec['bg']) + sec['normal'] = att + cfg.write(out) -- cgit v1.2.3 From 1f862a8932f1e5ce9fb0425d919890e3027385a9 Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Sun, 8 Jul 2012 22:21:45 +0100 Subject: doc: document theme spec and tagstring formatting --- docs/source/configuration/index.rst | 75 ++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 38 deletions(-) diff --git a/docs/source/configuration/index.rst b/docs/source/configuration/index.rst index 90744ef7..aa3ba4d8 100644 --- a/docs/source/configuration/index.rst +++ b/docs/source/configuration/index.rst @@ -277,36 +277,37 @@ To specify the theme to use, set the `theme` config option to the name of a them A file by that name will be looked up in the path given by the :ref:`themes_dir ` config setting which defaults to :file:`~/.config/alot/themes/`. -Theme-files can contain sections `[1], [16]` and `[256]` for different colour modes, -each of which has subsections named after the :ref:`MODE ` they are used in -plus "help" for the bindings-help overlay and "global" for globally used themables -like footer, prompt etc. -The themables live in sub-sub-sections and define the attributes `fg` and `bg` for foreground -and backround colours and attributes, the names of the themables should be self-explanatory. +Theme files contain a section for each :ref:`MODE ` plus "help" for the bindings-help overlay +and "global" for globally used themables like footer, prompt etc. +Each such section contains attribute values for the parts that can be themed. +The names of the themables should be self-explanatory. Attributes are sextuples of urwid Attribute +strings that specify fore/background for mono, 16 and 256-colour modes respectively. + +For mono-mode only the flags "blink,standup,underline,bold" are available, +16c mode supports these in combination with the colour names:: + + brown dark red dark magenta dark blue dark cyan dark green + yellow light red light magenta light blue light cyan light green + black dark gray light gray white + +In high-colour mode, you may use the above plus grayscales `g0` to `g100` and +colour codes given as `#` followed by three hex values. +See `here `_ +and `here `_ +for more details on the interpreted values. +A colour picker that makes choosing colours easy can be found in :file:`alot/extra/colour_picker.py`. + Have a look at the default theme file at :file:`alot/defaults/default.theme` -and the config spec :file:`alot/defaults/default.theme` for the format. +and the config spec :file:`alot/defaults/default.theme` for the exact format. As an example, check the setting below that makes the footer line appear as underlined bold red text on a bright green background:: - [256] - [[global]] - [[[footer]]] - fg = 'light red, bold, underline' - bg = '#8f6' - -Values can be colour names (`light red`, `dark green`..), RGB colour codes (e.g. `#868`), -font attributes (`bold`, `underline`, `blink`, `standout`) or a comma separated combination of -colour and font attributes. - -.. note:: In monochromatic mode only the entry `fg` is interpreted. It may only contain - (a comma-separated list of) font attributes: 'bold', 'underline', 'blink', 'standout'. - -See `urwids docs on Attributes `_ for more details -on the interpreted values. Urwid provides a `neat colour picker script`_ that makes choosing -colours easy. - -.. _neat colour picker script: http://excess.org/urwid/browser/palette_test.py + [[global]] + #name mono fg mono bg 16c fg 16c bg 256c fg 256c bg + # | | | | | | + # v v v v v v + footer = 'bold,underline', '', 'light red, bold, underline', 'light green', 'light red, bold, underline', '#8f6' Custom Tagstring Formatting @@ -314,10 +315,9 @@ Custom Tagstring Formatting To specify how a particular tagstring is displayed throughout the interface you can add a subsection named after the tag to the `[tags]` config section. -The following attribute keys will interpreted and may contain urwid attribute strings -as described in the :ref:`Themes ` section above: +`normal` and `focus` keys will interpreted and may contain urwid attribute strings +as described in the :ref:`Themes ` section above. -`fg` (foreground), `bg` (background), `focus_fg` (foreground if focused) and `focus_bg` (background if focused). An alternative string representation is read from the option `translated` or can be given as pair of strings in `translation`. @@ -328,8 +328,7 @@ The following will make alot display the "todo" tag as "TODO" in white on red. : [tags] [[todo]] - bg = '#d66' - fg = white + normal = '','', 'white','light red', 'white','#d66' translated = TODO Utf-8 symbols are welcome here, see e.g. @@ -337,13 +336,14 @@ http://panmental.de/symbols/info.htm for some fancy symbols. I personally displa like this:: [tags] + [[flagged]] translated = ⚑ - fg = light red + normal = '','','light red','','light red','' + focus = '','','light red','','light red','' [[unread]] translated = ✉ - fg = white [[replied]] translated = ⏎ @@ -358,12 +358,11 @@ rename a matching tagstring. `translation` takes a comma separated *pair* of str do the following:: [[notmuch::bug]] - fg = 'light red, bold' - bg = '#88d' - translated = 'nm:bug' + translated = 'nm:bug' + normal = "", "", "light red, bold", "light blue", "light red, bold", "#88d" + [[notmuch::.*]] - fg = '#fff' - bg = '#88d' - translation = 'notmuch::(.*)','nm:\1' + translation = 'notmuch::(.*)','nm:\1' + normal = "", "", "white", "light blue", "#fff", "#88d" .. _nmbug: http://notmuchmail.org/nmbug/ -- cgit v1.2.3 From 382d5fcb9bed5aec164538a15e320314289b90f6 Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Wed, 11 Jul 2012 21:56:08 +0100 Subject: fix default attributes for tagstrings --- alot/settings/__init__.py | 41 +++++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/alot/settings/__init__.py b/alot/settings/__init__.py index 8097fae4..510d75db 100644 --- a/alot/settings/__init__.py +++ b/alot/settings/__init__.py @@ -233,7 +233,8 @@ class SettingsManager(object): colours = int(self._config.get('colourmode')) return self._theme.get_threadline_structure(thread, colours) - def get_tagstring_representation(self, tag, normal=None, focus=None): + def get_tagstring_representation(self, tag, onebelow_normal=None, + onebelow_focus=None): """ looks up user's preferred way to represent a given tagstring @@ -246,27 +247,43 @@ class SettingsManager(object): cfg = self._config colours = [1, 16, 256] - def resolve_att(triple, fallback): + def colourpick(triple): + """ pick attribute from triple (mono,16c,256c) according to current + colourmode""" if triple is None: + return None + return triple[colours.index(colourmode)] + + def resolve_att(a, fallback): + """ replace '' and 'default' by fallback values """ + if a is None: return fallback - a = triple[colours.index(colourmode)] - if a.background in ['default', '']: - a.background = fallback.background - if a.foreground in ['default', '']: - a.foreground = fallback.foreground + try: + if a.background in ['default', '']: + a.background = fallback.background + if a.foreground in ['default', '']: + a.foreground = fallback.foreground + except: + logging.debug('DEFAULT_NORMAL') + logging.debug(a) return a + # global default attributes for tagstrings. + # These could contain values '' and 'default' which we interpret as + # "use the values from the widget below" default_normal = theme.get_attribute('global', 'tag', colourmode) default_focus = theme.get_attribute('global', 'tag_focus', colourmode) - fallback_normal = normal or default_normal - fallback_focus = focus or default_focus + + # local defaults for tagstring attributes. depend on next lower widget + fallback_normal = resolve_att(default_normal, onebelow_normal) + fallback_focus = resolve_att(default_focus, onebelow_focus) for sec in cfg['tags'].sections: if re.match('^' + sec + '$', tag): - normal = resolve_att(cfg['tags'][sec]['normal'], + normal = resolve_att(colourpick(cfg['tags'][sec]['normal']), fallback_normal) - logging.debug(normal) - focus = resolve_att(cfg['tags'][sec]['focus'], fallback_focus) + focus = resolve_att(colourpick(cfg['tags'][sec]['focus']), + fallback_focus) hidden = cfg['tags'][sec]['hidden'] or False -- cgit v1.2.3 From 08ba54db2f825de3e6eb9f5693f57dd1332c1f23 Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Thu, 12 Jul 2012 22:15:47 +0100 Subject: docstrings for new settings methods --- alot/settings/__init__.py | 25 +- alot/settings/theme.py | 2 +- docs/source/configuration/accounts_table | 135 +++++++++ docs/source/configuration/alotrc_table | 471 +++++++++++++++++++++++++++++++ docs/source/description | 4 + docs/source/usage/commands | 29 ++ docs/source/usage/first_steps | 10 + docs/source/usage/synopsis | 26 ++ 8 files changed, 696 insertions(+), 6 deletions(-) create mode 100644 docs/source/configuration/accounts_table create mode 100644 docs/source/configuration/alotrc_table create mode 100644 docs/source/description create mode 100644 docs/source/usage/commands create mode 100644 docs/source/usage/first_steps create mode 100644 docs/source/usage/synopsis diff --git a/alot/settings/__init__.py b/alot/settings/__init__.py index 510d75db..dacdcc9f 100644 --- a/alot/settings/__init__.py +++ b/alot/settings/__init__.py @@ -230,17 +230,32 @@ class SettingsManager(object): return self._theme.get_attribute(mode, name, colours) def get_threadline_theming(self, thread): + """ + looks up theming info a threadline displaying a given thread + + :param thread: thread to theme + :type thread: alot.db.thread.Thread + """ colours = int(self._config.get('colourmode')) - return self._theme.get_threadline_structure(thread, colours) + return self._theme.get_threadline_theming(thread, colours) def get_tagstring_representation(self, tag, onebelow_normal=None, onebelow_focus=None): """ looks up user's preferred way to represent a given tagstring - - This returns a dictionary mapping - 'normal' and 'focussed' to `urwid.AttrSpec` sttributes, - and 'translated' to an alternative string representation + on top of a widget with given attributes that shine though + for '' and 'default' values. + + This returns a dictionary mapping 'normal' and 'focussed' to + `urwid.AttrSpec` attributes, 'translated' to an alternative string + representation and 'hidden' to a boolean flag. + + :param tag: tagstring + :type tag: str + :param onebelow_normal: attribute that shine through if unfocussed + :type onebelow_normal: urwid.AttrSpec + :param onebelow_focus: attribute that shines through if focussed + :type onebelow_focus : urwid.AttrSpec """ colourmode = int(self._config.get('colourmode')) theme = self._theme diff --git a/alot/settings/theme.py b/alot/settings/theme.py index 8b60b2f1..563296c5 100644 --- a/alot/settings/theme.py +++ b/alot/settings/theme.py @@ -59,7 +59,7 @@ class Theme(object): """ return self._config[mode][name][self._colours.index(colourmode)] - def get_threadline_structure(self, thread, colourmode): + def get_threadline_theming(self, thread, colourmode): def pickcolour(triple): return triple[self._colours.index(colourmode)] diff --git a/docs/source/configuration/accounts_table b/docs/source/configuration/accounts_table new file mode 100644 index 00000000..aea1956c --- /dev/null +++ b/docs/source/configuration/accounts_table @@ -0,0 +1,135 @@ + +.. CAUTION: THIS FILE IS AUTO-GENERATED + from the inline comments of specfile defaults/alot.rc.spec. + + If you want to change its content make your changes + to that spec to ensure they woun't be overwritten later. + +.. _address: + +.. describe:: address + + your main email address + + :type: string + +.. _realname: + +.. describe:: realname + + used to format the (proposed) From-header in outgoing mails + + :type: string + +.. _aliases: + +.. describe:: aliases + + used to clear your addresses/ match account when formatting replies + + :type: string list + :default: , + + +.. _sendmail-command: + +.. describe:: sendmail_command + + sendmail command. This is the shell command used to send out mails via the sendmail protocol + + :type: string + :default: "sendmail -t" + + +.. _sent-box: + +.. describe:: sent_box + + where to store outgoing mails, e.g. `maildir:///home/you/mail/Sent`. + You can use mbox, maildir, mh, babyl and mmdf in the protocol part of the URL. + + .. note:: If you want to add outgoing mails automatically to the notmuch index + you must use maildir in a path within your notmuch database path. + + :type: mail_container + :default: None + + +.. _draft-box: + +.. describe:: draft_box + + where to store draft mails, e.g. `maildir:///home/you/mail/Drafts`. + You can use mbox, maildir, mh, babyl and mmdf in the protocol part of the URL. + + .. note:: You will most likely want drafts indexed by notmuch to be able to + later access them within alot. This currently only works for + maildir containers in a path below your notmuch database path. + + :type: mail_container + :default: None + + +.. _sent-tags: + +.. describe:: sent_tags + + list of tags to automatically add to outgoing messages + + :type: string list + :default: sent, + + +.. _signature: + +.. describe:: signature + + path to signature file that gets attached to all outgoing mails from this account, optionally + renamed to ref:`signature_filename `. + + :type: string + :default: None + + +.. _signature-as-attachment: + +.. describe:: signature_as_attachment + + attach signature file if set to True, append its content (mimetype text) + to the body text if set to False. + + :type: boolean + :default: False + + +.. _signature-filename: + +.. describe:: signature_filename + + signature file's name as it appears in outgoing mails if + :ref:`signature_as_attachment ` is set to True + + :type: string + :default: None + + +.. _sign-by-default: + +.. describe:: sign_by_default + + Outgoing messages will be GPG signed by default if this is set to True. + + :type: boolean + :default: False + + +.. _gpg-key: + +.. describe:: gpg_key + + The GPG key ID you want to use with this account. If unset, alot will + use your default key. + + :type: string + :default: None + diff --git a/docs/source/configuration/alotrc_table b/docs/source/configuration/alotrc_table new file mode 100644 index 00000000..6b4e2092 --- /dev/null +++ b/docs/source/configuration/alotrc_table @@ -0,0 +1,471 @@ + +.. CAUTION: THIS FILE IS AUTO-GENERATED + from the inline comments of specfile defaults/alot.rc.spec. + + If you want to change its content make your changes + to that spec to ensure they woun't be overwritten later. + +.. _ask-subject: + +.. describe:: ask_subject + + + :type: boolean + :default: True + + +.. _attachment-prefix: + +.. describe:: attachment_prefix + + directory prefix for downloading attachments + + :type: string + :default: "~" + + +.. _authors-maxlength: + +.. describe:: authors_maxlength + + maximal length of authors string in search mode before it gets truncated + + :type: integer + :default: 30 + + +.. _bufferclose-focus-offset: + +.. describe:: bufferclose_focus_offset + + offset of next focused buffer if the current one gets closed + + :type: integer + :default: -1 + + +.. _bufferlist-statusbar: + +.. describe:: bufferlist_statusbar + + Format of the status-bar in bufferlist mode. + This is a pair of strings to be left and right aligned in the status-bar that may contain variables: + + * `{buffer_no}`: index of this buffer in the global buffer list + * `{total_messages}`: total numer of messages indexed by notmuch + * `{pending_writes}`: number of pending write operations to the index + + :type: mixed_list + :default: [{buffer_no}: bufferlist], total messages: {total_messages} + + +.. _bug-on-exit: + +.. describe:: bug_on_exit + + confirm exit + + :type: boolean + :default: False + + +.. _colourmode: + +.. describe:: colourmode + + number of colours to use + + :type: option, one of ['1', '16', '256'] + :default: 256 + + +.. _complete-matching-abook-only: + +.. describe:: complete_matching_abook_only + + in case more than one account has an address book: + Set this to True to make tab completion for recipients during compose only + look in the abook of the account matching the sender address + + :type: boolean + :default: False + + +.. _display-content-in-threadline: + +.. describe:: display_content_in_threadline + + fill threadline with message content + + :type: boolean + :default: False + + +.. _displayed-headers: + +.. describe:: displayed_headers + + headers that get displayed by default + + :type: string list + :default: From, To, Cc, Bcc, Subject + + +.. _edit-headers-blacklist: + +.. describe:: edit_headers_blacklist + + see :ref:`edit_headers_whitelist ` + + :type: string list + :default: Content-Type, MIME-Version, References, In-Reply-To + + +.. _edit-headers-whitelist: + +.. describe:: edit_headers_whitelist + + Which header fields should be editable in your editor + used are those that match the whitelist and don't match the blacklist. + in both cases '*' may be used to indicate all fields. + + :type: string list + :default: \*, + + +.. _editor-cmd: + +.. describe:: editor_cmd + + editor command + if unset, alot will first try the :envvar:`EDITOR` env variable, then :file:`/usr/bin/editor` + + :type: string + :default: None + + +.. _editor-in-thread: + +.. describe:: editor_in_thread + + call editor in separate thread. + In case your editor doesn't run in the same window as alot, setting true here + will make alot non-blocking during edits + + :type: boolean + :default: False + + +.. _editor-spawn: + +.. describe:: editor_spawn + + use terminal_command to spawn a new terminal for the editor? + equivalent to always providing the `--spawn=yes` parameter to compose/edit commands + + :type: boolean + :default: False + + +.. _editor-writes-encoding: + +.. describe:: editor_writes_encoding + + file encoding used by your editor + + :type: string + :default: "UTF-8" + + +.. _envelope-headers-blacklist: + +.. describe:: envelope_headers_blacklist + + headers that are hidden in envelope buffers by default + + :type: string list + :default: In-Reply-To, References + + +.. _envelope-statusbar: + +.. describe:: envelope_statusbar + + Format of the status-bar in envelope mode. + This is a pair of strings to be left and right aligned in the status-bar. + Apart from the global variables listed at :ref:`bufferlist_statusbar ` + these strings may contain variables: + + * `{to}`: To-header of the envelope + + :type: mixed_list + :default: [{buffer_no}: envelope], total messages: {total_messages} + + +.. _flush-retry-timeout: + +.. describe:: flush_retry_timeout + + timeout in seconds after a failed attempt to writeout the database is repeated + + :type: integer + :default: 5 + + +.. _forward-subject-prefix: + +.. describe:: forward_subject_prefix + + String prepended to subject header on forward + only if original subject doesn't start with 'Fwd:' or this prefix + + :type: string + :default: "Fwd: " + + +.. _hooksfile: + +.. describe:: hooksfile + + where to look up hooks + + :type: string + :default: "~/.config/alot/hooks.py" + + +.. _initial-command: + +.. describe:: initial_command + + initial command when none is given as argument: + + :type: string + :default: "search tag:inbox AND NOT tag:killed" + + +.. _notify-timeout: + +.. describe:: notify_timeout + + time in secs to display status messages + + :type: integer + :default: 2 + + +.. _print-cmd: + +.. describe:: print_cmd + + how to print messages: + this specifies a shell command used for printing. + threads/messages are piped to this command as plain text. + muttprint/a2ps works nicely + + :type: string + :default: None + + +.. _prompt-suffix: + +.. describe:: prompt_suffix + + Suffix of the prompt used when waiting for user input + + :type: string + :default: ":" + + +.. _quit-on-last-bclose: + +.. describe:: quit_on_last_bclose + + shut down when the last buffer gets closed + + :type: boolean + :default: False + + +.. _quote-prefix: + +.. describe:: quote_prefix + + String prepended to line when quoting + + :type: string + :default: "> " + + +.. _reply-subject-prefix: + +.. describe:: reply_subject_prefix + + String prepended to subject header on reply + only if original subject doesn't start with 'Re:' or this prefix + + :type: string + :default: "Re: " + + +.. _search-statusbar: + +.. describe:: search_statusbar + + Format of the status-bar in search mode. + This is a pair of strings to be left and right aligned in the status-bar. + Apart from the global variables listed at :ref:`bufferlist_statusbar ` + these strings may contain variables: + + * `{querystring}`: search string + * `{result_count}`: number of matching messages + * `{result_count_positive}`: 's' if result count is greater than 0. + + :type: mixed_list + :default: [{buffer_no}: search] for "{querystring}", {result_count} of {total_messages} messages + + +.. _search-threads-sort-order: + +.. describe:: search_threads_sort_order + + default sort order of results in a search + + :type: option, one of ['oldest_first', 'newest_first', 'message_id', 'unsorted'] + :default: newest_first + + +.. _show-statusbar: + +.. describe:: show_statusbar + + display status-bar at the bottom of the screen? + + :type: boolean + :default: True + + +.. _tabwidth: + +.. describe:: tabwidth + + number of spaces used to replace tab characters + + :type: integer + :default: 8 + + +.. _taglist-statusbar: + +.. describe:: taglist_statusbar + + Format of the status-bar in taglist mode. + This is a pair of strings to be left and right aligned in the status-bar. + These strings may contain variables listed at :ref:`bufferlist_statusbar ` + that will be substituted accordingly. + + :type: mixed_list + :default: [{buffer_no}: taglist], total messages: {total_messages} + + +.. _template-dir: + +.. describe:: template_dir + + templates directory that contains your message templates. + It will be used if you give `compose --template` a filename without a path prefix. + + :type: string + :default: "$XDG_CONFIG_HOME/alot/templates" + + +.. _terminal-cmd: + +.. describe:: terminal_cmd + + set terminal command used for spawning shell commands + + :type: string + :default: "x-terminal-emulator -e" + + +.. _theme: + +.. describe:: theme + + name of the theme to use + + :type: string + :default: None + + +.. _themes-dir: + +.. describe:: themes_dir + + directory containing theme files + + :type: string + :default: None + + +.. _thread-authors-me: + +.. describe:: thread_authors_me + + Word to replace own addresses with. Works in combination with + :ref:`thread_authors_replace_me ` + + :type: string + :default: "Me" + + +.. _thread-authors-replace-me: + +.. describe:: thread_authors_replace_me + + Replace own email addresses with "me" in author lists + Uses own addresses and aliases in all configured accounts. + + :type: boolean + :default: True + + +.. _thread-statusbar: + +.. describe:: thread_statusbar + + Format of the status-bar in thread mode. + This is a pair of strings to be left and right aligned in the status-bar. + Apart from the global variables listed at :ref:`bufferlist_statusbar ` + these strings may contain variables: + + * `{tid}`: thread id + * `{subject}`: subject line of the thread + * `{authors}`: abbreviated authors string for this thread + * `{message_count}`: number of contained messages + + :type: mixed_list + :default: [{buffer_no}: thread] {subject}, total messages: {total_messages} + + +.. _timestamp-format: + +.. describe:: timestamp_format + + timestamp format in `strftime format syntax `_ + + :type: string + :default: None + + +.. _user-agent: + +.. describe:: user_agent + + value of the User-Agent header used for outgoing mails. + setting this to the empty string will cause alot to omit the header all together. + The string '{version}' will be replaced by the version string of the running instance. + + :type: string + :default: "alot/{version}" + diff --git a/docs/source/description b/docs/source/description new file mode 100644 index 00000000..9dea9693 --- /dev/null +++ b/docs/source/description @@ -0,0 +1,4 @@ +Alot is a terminal-based mail user agent for the notmuch mail system. +It features a modular and command prompt driven interface +to provide a full MUA experience as an alternative to the Emacs mode shipped +with notmuch. diff --git a/docs/source/usage/commands b/docs/source/usage/commands new file mode 100644 index 00000000..ec0be2eb --- /dev/null +++ b/docs/source/usage/commands @@ -0,0 +1,29 @@ +.. _commands: + +Commands +======== + +:doc:`modes/global` + globally available commands +:doc:`modes/search` + shows a result list of threads for a query +:doc:`modes/thread` + displays a thread as a tree of messages +:doc:`modes/envelope` + message composition mode +:doc:`modes/bufferlist` + lists all active buffers +:doc:`modes/taglist` + lists all tagstrings present in the notmuch database + +.. toctree:: + :maxdepth: 2 + :hidden: + + modes/global + modes/search + modes/thread + modes/envelope + modes/bufferlist + modes/taglist + diff --git a/docs/source/usage/first_steps b/docs/source/usage/first_steps new file mode 100644 index 00000000..e99c44ce --- /dev/null +++ b/docs/source/usage/first_steps @@ -0,0 +1,10 @@ +The arrow keys, `page-up/down`, `j`, `k` and `Space` can be used to move the focus. +`Escape` cancels prompts and `Enter` selects. Hit `:` at any time and type in commands +to the prompt. + +The interface shows one buffer at a time, you can use `tab` and `Shift-Tab` to switch +between them, close the current buffer with `d` and list them all with `;`. + +The buffer type or *mode* (displayed at the bottom left) determines which prompt commands +are available. Usage information on any command can be listed by typing `help YOURCOMMAND` +to the prompt; The key bindings for the current mode are listed upon pressing `?`. diff --git a/docs/source/usage/synopsis b/docs/source/usage/synopsis new file mode 100644 index 00000000..5bfb8e88 --- /dev/null +++ b/docs/source/usage/synopsis @@ -0,0 +1,26 @@ +.. code-block:: none + + alot [-r] [-c CONFIGFILE] [-n NOTMUCHCONFIGFILE] [-C {1,16,256}] [-p DB_PATH] + [-d {debug,info,warning,error}] [-l LOGFILE] [--version] [--help] + [command] + +Options + + -r, --read-only open db in read only mode + -c, --config=FILENAME config file (default: ~/.config/alot/config) + -n, --notmuch-config=FILENAME notmuch config (default: $NOTMUCH_CONFIG or ~/.notmuch-config) + -C, --colour-mode=COLOUR terminal colour mode (default: 256). Must be 1, 16 or 256 + -p, --mailindex-path=PATH path to notmuch index + -d, --debug-level=LEVEL debug log (default: info). Must be one of debug,info,warning or error + -l, --logfile=FILENAME logfile (default: /dev/null) + --version Display version string and exit + --help Display help and exit + + +Commands + + search + start in a search buffer using the querystring provided as + parameter. See the SEARCH SYNTAX section of notmuch(1). + compose + compose a new message -- cgit v1.2.3 From 24357ce276bdf94277d643c6aea42de1cb892616 Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Sat, 14 Jul 2012 12:43:14 +0100 Subject: doc: docstrings for new stuff in alot.settings.* --- alot/settings/__init__.py | 26 ++++++++++++++++---------- alot/settings/theme.py | 30 +++++++++++++++++++++++++++--- 2 files changed, 43 insertions(+), 13 deletions(-) diff --git a/alot/settings/__init__.py b/alot/settings/__init__.py index dacdcc9f..9f533936 100644 --- a/alot/settings/__init__.py +++ b/alot/settings/__init__.py @@ -225,13 +225,16 @@ class SettingsManager(object): :type mode: str :param name: identifier of the atttribute :type name: str + :rtype: urwid.AttrSpec """ colours = int(self._config.get('colourmode')) return self._theme.get_attribute(mode, name, colours) def get_threadline_theming(self, thread): """ - looks up theming info a threadline displaying a given thread + looks up theming info a threadline displaying a given thread. This + wraps around :meth:`~alot.settings.theme.Theme.get_threadline_theming`, + filling in the current colour mode. :param thread: thread to theme :type thread: alot.db.thread.Thread @@ -242,20 +245,23 @@ class SettingsManager(object): def get_tagstring_representation(self, tag, onebelow_normal=None, onebelow_focus=None): """ - looks up user's preferred way to represent a given tagstring - on top of a widget with given attributes that shine though - for '' and 'default' values. - - This returns a dictionary mapping 'normal' and 'focussed' to - `urwid.AttrSpec` attributes, 'translated' to an alternative string - representation and 'hidden' to a boolean flag. + looks up user's preferred way to represent a given tagstring. :param tag: tagstring :type tag: str - :param onebelow_normal: attribute that shine through if unfocussed + :param onebelow_normal: attribute that shines through if unfocussed :type onebelow_normal: urwid.AttrSpec :param onebelow_focus: attribute that shines through if focussed - :type onebelow_focus : urwid.AttrSpec + :type onebelow_focus: urwid.AttrSpec + + If `onebelow_normal` or `onebelow_focus` is given these attributes will + be used as fallbacks for fg/bg values '' and 'default'. + + This returns a dictionary mapping + :normal: to :class:`urwid.AttrSpec` used if unfocussed + :focussed: to :class:`urwid.AttrSpec` used if focussed + :translated: to an alternative string representation + :hidden: to a boolean """ colourmode = int(self._config.get('colourmode')) theme = self._theme diff --git a/alot/settings/theme.py b/alot/settings/theme.py index 563296c5..d225e646 100644 --- a/alot/settings/theme.py +++ b/alot/settings/theme.py @@ -43,9 +43,6 @@ class Theme(object): msg = 'missing threadline parts: %s' % difference raise ConfigError(msg) - def _by_colour(self, triple, colour): - return triple[self._colours.index(colour)] - def get_attribute(self, mode, name, colourmode): """ returns requested attribute @@ -56,10 +53,37 @@ class Theme(object): :type name: str :param colourmode: colour mode; in [1, 16, 256] :type colourmode: int + :rtype: urwid.AttrSpec """ return self._config[mode][name][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)] -- cgit v1.2.3 From f7c43a4ba7fd9dc4a4f33866c99c94b621078c0d Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Sat, 14 Jul 2012 18:57:47 +0100 Subject: fix threadline defaults this makes sure that undefined values in highlight 'threadline-foo' sections default to their corresponding parts in the 'threadlin' (default) section --- alot/settings/theme.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/alot/settings/theme.py b/alot/settings/theme.py index d225e646..520bb535 100644 --- a/alot/settings/theme.py +++ b/alot/settings/theme.py @@ -2,7 +2,6 @@ # This file is released under the GNU GPL, version 3 or a later revision. # For further details see the COPYING file import os -from urwid import AttrSpec, AttrSpecError from utils import read_config from checks import align_mode @@ -40,7 +39,7 @@ class Theme(object): indefault = set(threadline.sections) diff = listed.difference(here.union(indefault)) if diff: - msg = 'missing threadline parts: %s' % difference + msg = 'missing threadline parts: %s' % diff raise ConfigError(msg) def get_attribute(self, mode, name, colourmode): @@ -114,12 +113,16 @@ class Theme(object): res['focus'] = pickcolour(match.get('focus') or default['focus']) res['parts'] = match.get('parts') or default['parts'] for part in res['parts']: - partsec = match.get(part) or default[part] + defaultsec = default.get(part) + partsec = match.get(part) + + def fill(key, fallback=None): + pvalue = partsec.get(key) or defaultsec.get(key) + return pvalue or fallback + res[part] = {} - res[part]['width'] = partsec.get('width') or ('fit', 0, 0) - res[part]['alignment'] = partsec.get('alignment') - normal_triple = partsec.get('normal') or default['normal'] - res[part]['normal'] = pickcolour(normal_triple) - focus_triple = partsec.get('focus') or default['focus'] - res[part]['focus'] = pickcolour(focus_triple) + 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 -- cgit v1.2.3 From fa2324a0c36a6029b89af9f7dcb69ab32d167ec5 Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Sun, 15 Jul 2012 12:27:14 +0100 Subject: hotfix taglist --- alot/buffers.py | 24 ++++++++++++++++++------ alot/widgets.py | 2 +- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/alot/buffers.py b/alot/buffers.py index 07a7589d..e2c17e01 100644 --- a/alot/buffers.py +++ b/alot/buffers.py @@ -78,11 +78,11 @@ class BufferlistBuffer(Buffer): line = widgets.BufferlineWidget(b) if (num % 2) == 0: attr = settings.get_theming_attribute('bufferlist', - 'results_even') + 'line_even') else: - attr = settings.get_theming_attribute('bufferlist', - 'results_odd') - focus_att = settings.get_theming_attribute('bufferlist', 'focus') + attr = settings.get_theming_attribute('bufferlist', 'line_odd') + focus_att = settings.get_theming_attribute('bufferlist', + 'line_focus') buf = urwid.AttrMap(line, attr, focus_att) num = urwid.Text('%3d:' % self.index_of(b)) lines.append(urwid.Columns([('fixed', 4, num), buf])) @@ -385,13 +385,25 @@ class TagListBuffer(Buffer): displayedtags = sorted(filter(self.filtfun, self.tags), key=unicode.lower) for (num, b) in enumerate(displayedtags): + line = widgets.BufferlineWidget(b) + if (num % 2) == 0: + attr = settings.get_theming_attribute('bufferlist', + 'results_even') + else: + attr = settings.get_theming_attribute('bufferlist', + 'results_odd') + focus_att = settings.get_theming_attribute('bufferlist', 'focus') + tw = widgets.TagWidget(b) rows = [('fixed', tw.width(), tw)] if tw.hidden: rows.append(urwid.Text('[hidden]')) elif tw.translated is not b: rows.append(urwid.Text('(%s)' % b)) - lines.append(urwid.Columns(rows, dividechars=1)) + line = urwid.Columns(rows, dividechars=1) + line = urwid.AttrMap(line, attr, focus_att) + lines.append(line) + self.taglist = urwid.ListBox(urwid.SimpleListWalker(lines)) self.body = self.taglist @@ -400,5 +412,5 @@ class TagListBuffer(Buffer): def get_selected_tag(self): """returns selected tagstring""" (cols, pos) = self.taglist.get_focus() - tagwidget = cols.get_focus() + tagwidget = cols.original_widget.get_focus() return tagwidget.get_tag() diff --git a/alot/widgets.py b/alot/widgets.py index a939464b..6de59622 100644 --- a/alot/widgets.py +++ b/alot/widgets.py @@ -299,7 +299,7 @@ class TagWidget(urwid.AttrMap): normal_att = representation['normal'] focus_att = representation['focussed'] self.attmaps = {'normal': normal_att, 'focus': focus_att} - urwid.AttrMap.__init__(self, self.txt, normal_att) + urwid.AttrMap.__init__(self, self.txt, normal_att, focus_att) def set_map(self, attrstring): self.set_attr_map({None: self.attmaps[attrstring]}) -- cgit v1.2.3 From 09f84344e2a8457bb8eff60df23f7a62b2367e41 Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Sun, 15 Jul 2012 12:36:15 +0100 Subject: settings: propagate more failure info --- alot/settings/utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/alot/settings/utils.py b/alot/settings/utils.py index 6667fa67..45a70fa0 100644 --- a/alot/settings/utils.py +++ b/alot/settings/utils.py @@ -33,10 +33,10 @@ def read_config(configpath=None, specpath=None, checks={}): if results != True: error_msg = 'Validation errors occurred:\n' - for (section_list, key, _) in flatten_errors(config, results): + for (section_list, key, res) in flatten_errors(config, results): if key is not None: - msg = 'key "%s" in section "%s" failed validation' - msg = msg % (key, ', '.join(section_list)) + msg = 'key "%s" in section "%s" failed validation: %s' + msg = msg % (key, ', '.join(section_list), res) else: msg = 'section "%s" is malformed' % ', '.join(section_list) error_msg += msg + '\n' -- cgit v1.2.3 From 1f4599e5f3ec943cf6f368046654d684ab80cd1f Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Sun, 15 Jul 2012 12:37:28 +0100 Subject: rename bufferlist themables --- alot/defaults/default.theme | 6 +++--- alot/defaults/theme.spec | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/alot/defaults/default.theme b/alot/defaults/default.theme index 84c18608..8f46103b 100644 --- a/alot/defaults/default.theme +++ b/alot/defaults/default.theme @@ -11,9 +11,9 @@ section = 'underline','','bold,underline','dark gray','bold,underline','g35' title = 'standout','','white','dark blue','white,bold,underline','g35' [bufferlist] - focus = 'standout','','white','dark gray','#ffa','#68a' - results_even = 'default','','light gray','black','default','g3' - results_odd = 'default','','light gray','black','default','default' + line_focus = 'standout','','white','dark gray','#ffa','#68a' + line_even = 'default','','light gray','black','default','g3' + line_odd = 'default','','light gray','black','default','default' [thread] attachment = 'default','','light gray','dark gray','light gray','dark gray' attachment_focus = 'underline','','light gray','light green','light gray','light green' diff --git a/alot/defaults/theme.spec b/alot/defaults/theme.spec index f5ce8341..b800c28c 100644 --- a/alot/defaults/theme.spec +++ b/alot/defaults/theme.spec @@ -14,9 +14,9 @@ title = attrtriple # mode specific attributes [bufferlist] - focus = attrtriple - results_even = attrtriple - results_odd = attrtriple + line_focus = attrtriple + line_even = attrtriple + line_odd = attrtriple [search] [[threadline]] normal = attrtriple -- cgit v1.2.3 From 6a73d0a34f81e1e91860866fcede3b922e805fb9 Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Sun, 15 Jul 2012 12:41:15 +0100 Subject: introduce missing theming section for taglist --- alot/buffers.py | 8 +++----- alot/defaults/default.theme | 4 ++++ alot/defaults/theme.spec | 5 +++++ 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/alot/buffers.py b/alot/buffers.py index e2c17e01..f4e9bf1c 100644 --- a/alot/buffers.py +++ b/alot/buffers.py @@ -387,12 +387,10 @@ class TagListBuffer(Buffer): for (num, b) in enumerate(displayedtags): line = widgets.BufferlineWidget(b) if (num % 2) == 0: - attr = settings.get_theming_attribute('bufferlist', - 'results_even') + attr = settings.get_theming_attribute('taglist', 'line_even') else: - attr = settings.get_theming_attribute('bufferlist', - 'results_odd') - focus_att = settings.get_theming_attribute('bufferlist', 'focus') + attr = settings.get_theming_attribute('taglist', 'line_odd') + focus_att = settings.get_theming_attribute('taglist', 'line_focus') tw = widgets.TagWidget(b) rows = [('fixed', tw.width(), tw)] diff --git a/alot/defaults/default.theme b/alot/defaults/default.theme index 8f46103b..eb058154 100644 --- a/alot/defaults/default.theme +++ b/alot/defaults/default.theme @@ -14,6 +14,10 @@ line_focus = 'standout','','white','dark gray','#ffa','#68a' line_even = 'default','','light gray','black','default','g3' line_odd = 'default','','light gray','black','default','default' +[taglist] + line_focus = 'standout','','white','dark gray','#ffa','#68a' + line_even = 'default','','light gray','black','default','g3' + line_odd = 'default','','light gray','black','default','default' [thread] attachment = 'default','','light gray','dark gray','light gray','dark gray' attachment_focus = 'underline','','light gray','light green','light gray','light green' diff --git a/alot/defaults/theme.spec b/alot/defaults/theme.spec index b800c28c..3adbed41 100644 --- a/alot/defaults/theme.spec +++ b/alot/defaults/theme.spec @@ -17,6 +17,11 @@ line_focus = attrtriple line_even = attrtriple line_odd = attrtriple + +[taglist] + line_focus = attrtriple + line_even = attrtriple + line_odd = attrtriple [search] [[threadline]] normal = attrtriple -- cgit v1.2.3 From c99b64a33ccdf8122be6a59a94398a0ddab58ad5 Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Sun, 15 Jul 2012 13:34:41 +0100 Subject: use taglist theme sec --- alot/buffers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alot/buffers.py b/alot/buffers.py index f4e9bf1c..45c09df8 100644 --- a/alot/buffers.py +++ b/alot/buffers.py @@ -392,7 +392,7 @@ class TagListBuffer(Buffer): attr = settings.get_theming_attribute('taglist', 'line_odd') focus_att = settings.get_theming_attribute('taglist', 'line_focus') - tw = widgets.TagWidget(b) + tw = widgets.TagWidget(b, attr, focus_att) rows = [('fixed', tw.width(), tw)] if tw.hidden: rows.append(urwid.Text('[hidden]')) -- cgit v1.2.3 From 4275873b7b0a8f6335f80651e9843c7728c2f1b5 Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Sun, 15 Jul 2012 13:35:34 +0100 Subject: fix tagstring defaults resolution --- alot/settings/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/alot/settings/__init__.py b/alot/settings/__init__.py index 9f533936..67dd4094 100644 --- a/alot/settings/__init__.py +++ b/alot/settings/__init__.py @@ -296,8 +296,8 @@ class SettingsManager(object): default_focus = theme.get_attribute('global', 'tag_focus', colourmode) # local defaults for tagstring attributes. depend on next lower widget - fallback_normal = resolve_att(default_normal, onebelow_normal) - fallback_focus = resolve_att(default_focus, onebelow_focus) + fallback_normal = resolve_att(onebelow_normal, default_normal) + fallback_focus = resolve_att(onebelow_focus, default_focus) for sec in cfg['tags'].sections: if re.match('^' + sec + '$', tag): -- cgit v1.2.3 From fc3318c7167d7369cd7064f35f16a1d7bbbed231 Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Sun, 15 Jul 2012 13:36:18 +0100 Subject: use correct default values in threadline's tags part this makes threadline.tags use the defaults from [threadline] [[tags]] --- alot/widgets.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/alot/widgets.py b/alot/widgets.py index 6de59622..518911e2 100644 --- a/alot/widgets.py +++ b/alot/widgets.py @@ -185,8 +185,8 @@ class ThreadlineWidget(urwid.AttrMap): part = content_w elif name == 'tags': if self.thread: - fallback_normal = self.structure['normal'] - fallback_focus = self.structure['focus'] + fallback_normal = struct[name]['normal'] + fallback_focus = struct[name]['focus'] tag_widgets = [TagWidget(t, fallback_normal, fallback_focus) for t in self.thread.get_tags()] tag_widgets.sort(tag_cmp, -- cgit v1.2.3 From 5415fd0ed790f148fbc507a4d87912f803ea23dc Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Sun, 15 Jul 2012 18:29:38 +0100 Subject: move thread move theming to more durable config syntax --- alot/defaults/default.theme | 9 ++++++--- alot/defaults/theme.spec | 7 ++++--- alot/settings/__init__.py | 8 ++++---- alot/settings/theme.py | 9 ++++++--- alot/widgets.py | 8 ++++---- 5 files changed, 24 insertions(+), 17 deletions(-) diff --git a/alot/defaults/default.theme b/alot/defaults/default.theme index eb058154..402eb3ec 100644 --- a/alot/defaults/default.theme +++ b/alot/defaults/default.theme @@ -25,9 +25,12 @@ header = 'default','','white','dark gray','white','dark gray' header_key = 'default','','white','dark gray','white','dark gray' header_value = 'default','','light gray','dark gray','light gray','dark gray' - summary_even = 'default','','white','light blue','white','#068' - summary_focus = 'standout','','white','dark cyan','#ff8','g58' - summary_odd = 'default','','white','dark blue','white','#006' + + [[summary]] + even = 'default','','white','light blue','white','#068' + odd = 'default','','white','dark blue','white','#006' + focus = 'standout','','white','dark cyan','#ff8','g58' + [envelope] body = 'default','','light gray','default','light gray','default' header = 'default','','white','dark gray','white','dark gray' diff --git a/alot/defaults/theme.spec b/alot/defaults/theme.spec index 3adbed41..27ed7e48 100644 --- a/alot/defaults/theme.spec +++ b/alot/defaults/theme.spec @@ -52,9 +52,10 @@ header = attrtriple header_key = attrtriple header_value = attrtriple - summary_even = attrtriple - summary_focus = attrtriple - summary_odd = attrtriple + [[summary]] + even = attrtriple + odd = attrtriple + focus = attrtriple [envelope] body = attrtriple header = attrtriple diff --git a/alot/settings/__init__.py b/alot/settings/__init__.py index 67dd4094..60cf5c50 100644 --- a/alot/settings/__init__.py +++ b/alot/settings/__init__.py @@ -217,7 +217,7 @@ class SettingsManager(object): value = fallback return value - def get_theming_attribute(self, mode, name): + def get_theming_attribute(self, mode, name, part=None): """ looks up theming attribute @@ -228,7 +228,7 @@ class SettingsManager(object): :rtype: urwid.AttrSpec """ colours = int(self._config.get('colourmode')) - return self._theme.get_attribute(mode, name, colours) + return self._theme.get_attribute(colours, mode, name, part) def get_threadline_theming(self, thread): """ @@ -292,8 +292,8 @@ class SettingsManager(object): # global default attributes for tagstrings. # These could contain values '' and 'default' which we interpret as # "use the values from the widget below" - default_normal = theme.get_attribute('global', 'tag', colourmode) - default_focus = theme.get_attribute('global', 'tag_focus', colourmode) + default_normal = theme.get_attribute(colourmode, 'global', 'tag') + default_focus = theme.get_attribute(colourmode, 'global', 'tag_focus') # local defaults for tagstring attributes. depend on next lower widget fallback_normal = resolve_att(onebelow_normal, default_normal) diff --git a/alot/settings/theme.py b/alot/settings/theme.py index 520bb535..ea154504 100644 --- a/alot/settings/theme.py +++ b/alot/settings/theme.py @@ -42,19 +42,22 @@ class Theme(object): msg = 'missing threadline parts: %s' % diff raise ConfigError(msg) - def get_attribute(self, mode, name, colourmode): + 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: identifier of the atttribute + :param name: of the atttribute :type name: str :param colourmode: colour mode; in [1, 16, 256] :type colourmode: int :rtype: urwid.AttrSpec """ - return self._config[mode][name][self._colours.index(colourmode)] + thmble = self._config[mode][name] + if part is not None: + thmble = thmble[part] + return thmble[self._colours.index(colourmode)] def get_threadline_theming(self, thread, colourmode): """ diff --git a/alot/widgets.py b/alot/widgets.py index 518911e2..b3223ad8 100644 --- a/alot/widgets.py +++ b/alot/widgets.py @@ -649,9 +649,10 @@ class MessageSummaryWidget(urwid.WidgetWrap): self.message = message self.even = even if even: - attr = settings.get_theming_attribute('thread', 'summary_even') + attr = settings.get_theming_attribute('thread', 'summary', 'even') else: - attr = settings.get_theming_attribute('thread', 'summary_odd') + attr = settings.get_theming_attribute('thread', 'summary', 'odd') + focus_att = settings.get_theming_attribute('thread', 'summary', 'focus') cols = [] sumstr = self.__str__() @@ -660,12 +661,11 @@ class MessageSummaryWidget(urwid.WidgetWrap): thread_tags = message.get_thread().get_tags(intersection=True) outstanding_tags = set(message.get_tags()).difference(thread_tags) - tag_widgets = [TagWidget(t) for t in outstanding_tags] + tag_widgets = [TagWidget(t, attr, focus_att) for t in outstanding_tags] tag_widgets.sort(tag_cmp, lambda tag_widget: tag_widget.translated) for tag_widget in tag_widgets: if not tag_widget.hidden: cols.append(('fixed', tag_widget.width(), tag_widget)) - focus_att = settings.get_theming_attribute('thread', 'summary_focus') line = urwid.AttrMap(urwid.Columns(cols, dividechars=1), attr, focus_att) urwid.WidgetWrap.__init__(self, line) -- cgit v1.2.3 From 439ca4ee449742228d7861ad8a31206c9dd8acd1 Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Sun, 15 Jul 2012 18:41:09 +0100 Subject: update theme converter --- extra/theme_convert.py | 96 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 67 insertions(+), 29 deletions(-) diff --git a/extra/theme_convert.py b/extra/theme_convert.py index b9317b04..0873bd02 100755 --- a/extra/theme_convert.py +++ b/extra/theme_convert.py @@ -35,6 +35,7 @@ if __name__ == "__main__": args = parser.parse_args() old = ConfigObj(args.themefile) + new = ConfigObj() out = args.out def lookup(path): @@ -42,36 +43,73 @@ if __name__ == "__main__": for c in ['1', '16', '256']: values.append(get_leaf_value(old, [c] + path + ['fg']) or 'default') values.append(get_leaf_value(old, [c] + path + ['bg']) or 'default') + return values values = map(lambda s: '\'' + s + '\'', values) return ','.join(values) - for bmode in ['global', 'help', 'bufferlist', 'thread', 'envelope']: - out.write('[%s]\n' % bmode) + for bmode in ['global', 'help', 'envelope']: + new[bmode] = {} + #out.write('[%s]\n' % bmode) for themable in old['16'][bmode].sections: - out.write(' %s = %s\n' % (themable, lookup([bmode, themable]))) - - out.write('[search]\n') - out.write(' [[threadline]]\n') - - out.write(' ' * 8 + 'normal = %s\n' % lookup(['search', 'thread'])) - out.write(' ' * 8 + 'focus = %s\n' % lookup(['search', 'thread_focus'])) - out.write(' ' * 8 + 'parts = date,mailcount,tags,authors,subject\n') - - out.write(' ' * 8 + '[[[date]]]\n') - out.write(' ' * 12 + 'normal = %s\n' % lookup(['search', 'thread_date'])) - out.write(' ' * 12 + 'focus = %s\n' % lookup(['search', 'thread_date_focus'])) - out.write(' ' * 8 + '[[[mailcount]]]\n') - out.write(' ' * 12 + 'normal = %s\n' % lookup(['search', 'thread_mailcount'])) - out.write(' ' * 12 + 'focus = %s\n' % lookup(['search', 'thread_mailcount_focus'])) - out.write(' ' * 8 + '[[[tags]]]\n') - out.write(' ' * 12 + 'normal = %s\n' % lookup(['search', 'thread_tags'])) - out.write(' ' * 12 + 'focus = %s\n' % lookup(['search', 'thread_tags_focus'])) - out.write(' ' * 8 + '[[[authors]]]\n') - out.write(' ' * 12 + 'normal = %s\n' % lookup(['search', 'thread_authors'])) - out.write(' ' * 12 + 'focus = %s\n' % lookup(['search', 'thread_authors_focus'])) - out.write(' ' * 8 + '[[[subject]]]\n') - out.write(' ' * 12 + 'normal = %s\n' % lookup(['search', 'thread_subject'])) - out.write(' ' * 12 + 'focus = %s\n' % lookup(['search', 'thread_subject_focus'])) - out.write(' ' * 8 + '[[[content]]]\n') - out.write(' ' * 12 + 'normal = %s\n' % lookup(['search', 'thread_content'])) - out.write(' ' * 12 + 'focus = %s\n' % lookup(['search', 'thread_content_focus'])) + new[bmode][themable] = lookup([bmode, themable]) + #out.write(' %s = %s\n' % (themable, lookup([bmode, themable]))) + + # BUFFERLIST + new['bufferlist'] = {} + new['bufferlist']['line_even'] = lookup(['bufferlist','results_even']) + new['bufferlist']['line_odd'] = lookup(['bufferlist','results_odd']) + new['bufferlist']['line_focus'] = lookup(['bufferlist','focus']) + + # TAGLIST + new['taglist'] = {} + new['taglist']['line_even'] = lookup(['bufferlist','results_even']) + new['taglist']['line_odd'] = lookup(['bufferlist','results_odd']) + new['taglist']['line_focus'] = lookup(['bufferlist','focus']) + + # SEARCH + new['search'] = {} + + new['search']['threadline'] = {} + new['search']['threadline']['normal'] = lookup(['search', 'thread']) + new['search']['threadline']['focus'] = lookup(['search', 'thread_focus']) + new['search']['threadline']['parts'] = ['date','mailcount','tags','authors','subject'] + + new['search']['threadline']['date'] = {} + new['search']['threadline']['date']['normal'] = lookup(['search', 'thread_date']) + new['search']['threadline']['date']['focus'] = lookup(['search', 'thread_date_focus']) + + new['search']['threadline']['mailcount'] = {} + new['search']['threadline']['mailcount']['normal'] = lookup(['search', 'thread_mailcount']) + new['search']['threadline']['mailcount']['focus'] = lookup(['search', 'thread_mailcount_focus']) + + new['search']['threadline']['tags'] = {} + new['search']['threadline']['tags']['normal'] = lookup(['search', 'thread_tags']) + new['search']['threadline']['tags']['focus'] = lookup(['search', 'thread_tags_focus']) + + new['search']['threadline']['authors'] = {} + new['search']['threadline']['authors']['normal'] = lookup(['search', 'thread_authors']) + new['search']['threadline']['authors']['focus'] = lookup(['search', 'thread_authors_focus']) + + new['search']['threadline']['subject'] = {} + new['search']['threadline']['subject']['normal'] = lookup(['search', 'thread_subject']) + new['search']['threadline']['subject']['focus'] = lookup(['search', 'thread_subject_focus']) + + new['search']['threadline']['content'] = {} + new['search']['threadline']['content']['normal'] = lookup(['search', 'thread_content']) + new['search']['threadline']['content']['focus'] = lookup(['search', 'thread_content_focus']) + + # THREAD + new['thread'] = {} + new['thread']['attachment'] = lookup(['thread','attachment']) + new['thread']['attachment_focus'] = lookup(['thread','attachment_focus']) + new['thread']['body'] = lookup(['thread','body']) + new['thread']['header'] = lookup(['thread','header']) + new['thread']['header_key'] = lookup(['thread','header_key']) + new['thread']['header_value'] = lookup(['thread','header_value']) + new['thread']['summary'] = {} + new['thread']['summary']['even'] = lookup(['thread','summary_even']) + new['thread']['summary']['odd'] = lookup(['thread','summary_odd']) + new['thread']['summary']['focus'] = lookup(['thread','summary_focus']) + + # write out + new.write(out) -- cgit v1.2.3 From 882705ba0173a1dca453130c0355be6481f27b6a Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Mon, 16 Jul 2012 16:14:16 +0100 Subject: doc: document threadline highlighting --- docs/source/configuration/index.rst | 103 +++++++++++++++++++++++++++++++----- 1 file changed, 91 insertions(+), 12 deletions(-) diff --git a/docs/source/configuration/index.rst b/docs/source/configuration/index.rst index aa3ba4d8..6314754e 100644 --- a/docs/source/configuration/index.rst +++ b/docs/source/configuration/index.rst @@ -273,17 +273,28 @@ Alot can be run in 1, 16 or 256 colour mode. The requested mode is determined by from option `colourmode` config value. The default is 256, which scales down depending on how many colours your terminal supports. -To specify the theme to use, set the `theme` config option to the name of a theme-file. +To specify the theme to use, set the :ref:`theme ` config option to the name of a theme-file. A file by that name will be looked up in the path given by the :ref:`themes_dir ` config setting which defaults to :file:`~/.config/alot/themes/`. -Theme files contain a section for each :ref:`MODE ` plus "help" for the bindings-help overlay +.. _config.theming.themefiles: + +Theme Files +----------- +contain a section for each :ref:`MODE ` plus "help" for the bindings-help overlay and "global" for globally used themables like footer, prompt etc. Each such section contains attribute values for the parts that can be themed. -The names of the themables should be self-explanatory. Attributes are sextuples of urwid Attribute -strings that specify fore/background for mono, 16 and 256-colour modes respectively. +The names of the themables should be self-explanatory. +Have a look at the default theme file at :file:`alot/defaults/default.theme` +and the config spec :file:`alot/defaults/default.theme` for the exact format. -For mono-mode only the flags "blink,standup,underline,bold" are available, +.. _config.theming.attributes: + +Colour Attributes +----------------- +Attributes are *sextuples* of `urwid Attribute strings `__ +that specify foreground and background for mono, 16 and 256-colour modes respectively. +For mono-mode only the flags `blink`, `standup`, `underline` and `bold` are available, 16c mode supports these in combination with the colour names:: brown dark red dark magenta dark blue dark cyan dark green @@ -292,13 +303,10 @@ For mono-mode only the flags "blink,standup,underline,bold" are available, In high-colour mode, you may use the above plus grayscales `g0` to `g100` and colour codes given as `#` followed by three hex values. -See `here `_ -and `here `_ -for more details on the interpreted values. -A colour picker that makes choosing colours easy can be found in :file:`alot/extra/colour_picker.py`. - -Have a look at the default theme file at :file:`alot/defaults/default.theme` -and the config spec :file:`alot/defaults/default.theme` for the exact format. +See `here `__ +and `here `__ +for more details on the interpreted values. A colour picker that makes choosing colours easy can be +found in :file:`alot/extra/colour_picker.py`. As an example, check the setting below that makes the footer line appear as underlined bold red text on a bright green background:: @@ -309,6 +317,77 @@ underlined bold red text on a bright green background:: # v v v v v v footer = 'bold,underline', '', 'light red, bold, underline', 'light green', 'light red, bold, underline', '#8f6' +Highlighting Thread lines in Search Mode +---------------------------------------- +The subsection '[[threadline]]' of the '[search]' section in :ref:`Theme Files ` +determines how to present a thread: here, :ref:`attributes ` 'normal' and +'focus' provide fallback/spacer themes and 'parts' is a (string) list of displayed subwidgets. +Possible part strings are: + +* date +* mailcount +* tags +* authors +* subject + +For every listed part there must be a subsection with the same name, defining + +:normal: :ref:`attribute ` used for this part if unfocussed +:focus: :ref:`attribute ` used for this part if focussed +:width: tuple indicating the width of the part. 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: how to place the content string if the widget space is larger. + This must be one of 'right', 'left' or 'center'. + +To "highlight" some thread lines (use different attributes than the defaults found in the +'[[threadline]]' section), one can define sections with prefix 'threadline'. +Each one of those can redefine any part of the structure outlined above, the rest defaults to +values defined in '[[threadline]]'. + +The section used to theme a particular thread is the first one (in file-order) that matches +the criteria defined by its 'query' and 'taggeswith' values: + +* If 'query' is defined, the thread must match that querystring. +* If 'tagged_with' is defined, is value (string list) must be a subset of the accumulated tags of all messages in the thread. + +.. note:: that 'tagged_with = A,B' is different from 'query = "is:A AND is:B"': + the latter will match only if the thread contains a single message that is both tagged with + A and B. + + Moreover, note that if both query and tagged_with is undefined, this section will always match + and thus overwrite the defaults. + +The example below shows how to highlight unread threads: +The date-part will be bold red if the thread has unread messages and flagged messages +and just bold if the thread has unread but no flagged messages:: + + [search] + # default threadline + [[threadline]] + normal = 'default','default','default','default','#6d6','default' + focus = 'standout','default','light gray','dark gray','white','#68a' + parts = date,mailcount,tags,authors,subject + [[[date]]] + normal = 'default','default','light gray','default','g58','default' + focus = 'standout','default','light gray','dark gray','g89','#68a' + width = 'fit',10,10 + ... + + # highlight threads containing unread and flagged messages + [[threadline-flagged-unread]] + tagged_with = 'unread','flagged' + [[[date]]] + normal = 'default','default','light red,bold','default','light red,bold','default' + + # highlight threads containing unread messages + [[threadline-unread]] + query = 'is:unread' + [[[date]]] + normal = 'default','default','light gray,bold','default','g58,bold','default' + + Custom Tagstring Formatting =========================== -- cgit v1.2.3 From c80a0d3c3bee504c611b563e87987cabc509489c Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Mon, 16 Jul 2012 22:18:33 +0100 Subject: remove 'hidden' tag representation option this makes hiding tags possible simply by making their 'translated' value the empty string. Note that this still sets the TagWidget.hidden property because those may not be displayed with width 0 (as any other widget) because it'd break urwid. --- alot/buffers.py | 2 +- alot/settings/__init__.py | 11 ++++------- alot/widgets.py | 2 +- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/alot/buffers.py b/alot/buffers.py index 45c09df8..866e1491 100644 --- a/alot/buffers.py +++ b/alot/buffers.py @@ -395,7 +395,7 @@ class TagListBuffer(Buffer): tw = widgets.TagWidget(b, attr, focus_att) rows = [('fixed', tw.width(), tw)] if tw.hidden: - rows.append(urwid.Text('[hidden]')) + rows.append(urwid.Text(b + ' [hidden]')) elif tw.translated is not b: rows.append(urwid.Text('(%s)' % b)) line = urwid.Columns(rows, dividechars=1) diff --git a/alot/settings/__init__.py b/alot/settings/__init__.py index 60cf5c50..faae8904 100644 --- a/alot/settings/__init__.py +++ b/alot/settings/__init__.py @@ -261,7 +261,6 @@ class SettingsManager(object): :normal: to :class:`urwid.AttrSpec` used if unfocussed :focussed: to :class:`urwid.AttrSpec` used if focussed :translated: to an alternative string representation - :hidden: to a boolean """ colourmode = int(self._config.get('colourmode')) theme = self._theme @@ -306,9 +305,9 @@ class SettingsManager(object): focus = resolve_att(colourpick(cfg['tags'][sec]['focus']), fallback_focus) - hidden = cfg['tags'][sec]['hidden'] or False - - translated = cfg['tags'][sec]['translated'] or tag + translated = cfg['tags'][sec]['translated'] + if translated is None: + translated = tag translation = cfg['tags'][sec]['translation'] if translation: translated = re.sub(translation[0], translation[1], tag) @@ -316,11 +315,9 @@ class SettingsManager(object): else: normal = fallback_normal focus = fallback_focus - hidden = False translated = tag - return {'normal': normal, 'focussed': focus, - 'hidden': hidden, 'translated': translated} + return {'normal': normal, 'focussed': focus, 'translated': translated} def get_hook(self, key): """return hook (`callable`) identified by `key`""" diff --git a/alot/widgets.py b/alot/widgets.py index b3223ad8..278d31e4 100644 --- a/alot/widgets.py +++ b/alot/widgets.py @@ -293,8 +293,8 @@ class TagWidget(urwid.AttrMap): representation = settings.get_tagstring_representation(tag, fallback_normal, fallback_focus) - self.hidden = representation['hidden'] self.translated = representation['translated'] + self.hidden = self.translated == '' self.txt = urwid.Text(self.translated, wrap='clip') normal_att = representation['normal'] focus_att = representation['focussed'] -- cgit v1.2.3 From 77ae345dc791fb115e0c4beffc1f8bc6beb92883 Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Mon, 16 Jul 2012 22:22:20 +0100 Subject: replace 'hidden' values in tags-section converter --- extra/tagsections_convert.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/extra/tagsections_convert.py b/extra/tagsections_convert.py index 4dbf4554..82ccd33e 100755 --- a/extra/tagsections_convert.py +++ b/extra/tagsections_convert.py @@ -47,12 +47,14 @@ if __name__ == "__main__": for tag in cfg['tags'].sections: sec = cfg['tags'][tag] att = [''] * 6 + if 'fg' in sec: fg = sec['fg'] if not is_256(fg): att[2] = fg att[4] = fg del(sec['fg']) + if 'bg' in sec: bg = sec['bg'] if not is_256(bg): @@ -60,4 +62,7 @@ if __name__ == "__main__": att[5] = bg del(sec['bg']) sec['normal'] = att + + if sec['hidden']: + sec['translated'] = '' cfg.write(out) -- cgit v1.2.3 From a16f5bd0ba6cca77dbadc4e2ae8e9eca91217407 Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Mon, 16 Jul 2012 22:31:12 +0100 Subject: doc: rewrite tagstring formatting section --- docs/source/configuration/index.rst | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/docs/source/configuration/index.rst b/docs/source/configuration/index.rst index 6314754e..ba15eb42 100644 --- a/docs/source/configuration/index.rst +++ b/docs/source/configuration/index.rst @@ -387,21 +387,22 @@ and just bold if the thread has unread but no flagged messages:: [[[date]]] normal = 'default','default','light gray,bold','default','g58,bold','default' - +.. _config.theming.tags: Custom Tagstring Formatting -=========================== +--------------------------- To specify how a particular tagstring is displayed throughout the interface you can add a subsection named after the tag to the `[tags]` config section. -`normal` and `focus` keys will interpreted and may contain urwid attribute strings -as described in the :ref:`Themes ` section above. - -An alternative string representation is read from the option `translated` or can be given -as pair of strings in `translation`. - -The tag can also be hidden from view, if the key `hidden` is present and set to -True. The tag can still be seen in the taglist buffer. +Such a section may define + +:normal: :ref:`attribute ` used if unfocussed +:focus: :ref:`attribute ` used if focussed +:translated: fixed string representation for this tag. The tag can be hidden from view, + if the key `translated` is set to '', the empty string. +:translation: a pair of strings that define a regular substitution to compute the string + representation on the fly using `re.sub`. This only really makes sense if + one uses a regular expression to match more than one tagstring (see below). The following will make alot display the "todo" tag as "TODO" in white on red. :: -- cgit v1.2.3 From 903c0ad20ea55e7be525a2ab1f3d01c1aed93774 Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Wed, 18 Jul 2012 22:20:13 +0100 Subject: outsource resolve_att settings util --- alot/settings/__init__.py | 15 +-------------- alot/settings/utils.py | 11 +++++++++++ 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/alot/settings/__init__.py b/alot/settings/__init__.py index faae8904..8205d099 100644 --- a/alot/settings/__init__.py +++ b/alot/settings/__init__.py @@ -18,6 +18,7 @@ from alot.helper import pretty_datetime, string_decode from errors import ConfigError from utils import read_config +from utils import resolve_att from checks import force_list from checks import mail_container from checks import gpg_key @@ -274,20 +275,6 @@ class SettingsManager(object): return None return triple[colours.index(colourmode)] - def resolve_att(a, fallback): - """ replace '' and 'default' by fallback values """ - if a is None: - return fallback - try: - if a.background in ['default', '']: - a.background = fallback.background - if a.foreground in ['default', '']: - a.foreground = fallback.foreground - except: - logging.debug('DEFAULT_NORMAL') - logging.debug(a) - return a - # global default attributes for tagstrings. # These could contain values '' and 'default' which we interpret as # "use the values from the widget below" diff --git a/alot/settings/utils.py b/alot/settings/utils.py index 45a70fa0..accc3ab8 100644 --- a/alot/settings/utils.py +++ b/alot/settings/utils.py @@ -42,3 +42,14 @@ def read_config(configpath=None, specpath=None, checks={}): error_msg += msg + '\n' raise ConfigError(error_msg) return config + + +def resolve_att(a, fallback): + """ replace '' and 'default' by fallback values """ + if a is None: + return fallback + if a.background in ['default', '']: + a.background = fallback.background + if a.foreground in ['default', '']: + a.foreground = fallback.foreground + return a -- cgit v1.2.3 From 96d3452d320b6e6fa338d9f61c869641d801e1bd Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Fri, 20 Jul 2012 18:54:23 +0100 Subject: individually theme arrows heads/bars in thread view --- alot/defaults/default.theme | 8 +++++--- alot/defaults/theme.spec | 2 ++ alot/widgets.py | 20 ++++++++++++++++---- extra/theme_convert.py | 2 ++ 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/alot/defaults/default.theme b/alot/defaults/default.theme index 402eb3ec..cdfe8665 100644 --- a/alot/defaults/default.theme +++ b/alot/defaults/default.theme @@ -1,9 +1,9 @@ [global] - footer = 'standout','','light green','dark blue','white','#006' - body = 'default','','default','default','default','default' + footer = 'standout','','light green,bold','dark blue','light green,bold','dark blue' + body = 'default','','default','default','dark red','default' notify_error = 'standout','','white','dark red','white','dark red' notify_normal = 'default','','light gray','dark gray','light gray','#68a' - prompt = 'default','','light gray','black','light gray','g10' + prompt = 'default','','light gray','black','light gray','black' tag = 'default','','brown','black','brown','default' tag_focus = 'standout, bold','','white','dark gray','#ffa','#68a' [help] @@ -19,6 +19,8 @@ line_even = 'default','','light gray','black','default','g3' line_odd = 'default','','light gray','black','default','default' [thread] + arrow_heads = '','','light red','','light red','' + arrow_bars = '','','dark red','','dark red','' attachment = 'default','','light gray','dark gray','light gray','dark gray' attachment_focus = 'underline','','light gray','light green','light gray','light green' body = 'default','','light gray','default','light gray','default' diff --git a/alot/defaults/theme.spec b/alot/defaults/theme.spec index 27ed7e48..176d7b8a 100644 --- a/alot/defaults/theme.spec +++ b/alot/defaults/theme.spec @@ -46,6 +46,8 @@ width = widthtuple(default=None) alignment = align(default='right') [thread] + arrow_heads = attrtriple + arrow_bars = attrtriple attachment = attrtriple attachment_focus = attrtriple body = attrtriple diff --git a/alot/widgets.py b/alot/widgets.py index 278d31e4..74b30e32 100644 --- a/alot/widgets.py +++ b/alot/widgets.py @@ -12,6 +12,7 @@ import alot.db.message as message from alot.db.attachment import Attachment import time from alot.db.utils import decode_header +from alot.settings.utils import resolve_att class AttrFlipWidget(urwid.AttrMap): @@ -467,6 +468,12 @@ class MessageWidget(urwid.WidgetWrap): self._filtered_headers = [k for k in displayed if k in self.mail] self._displayed_headers = None + bars = settings.get_theming_attribute('thread', 'arrow_bars') + self.arrow_bars_att = bars + heads = settings.get_theming_attribute('thread', 'arrow_heads') + self.arrow_heads_att = heads + logging.debug(self.arrow_heads_att) + self.rebuild() # this will build self.pile urwid.WidgetWrap.__init__(self, self.pile) @@ -501,12 +508,15 @@ class MessageWidget(urwid.WidgetWrap): bc = list() # box_columns if self.depth > 1: bc.append(0) - cols.append(self._get_spacer(self.bars_at[1:-1])) + spacer = self._get_spacer(self.bars_at[1:-1]) + cols.append(spacer) if self.depth > 0: if self.bars_at[-1]: - arrowhead = u'\u251c\u25b6' + arrowhead = [(self.arrow_bars_att, u'\u251c'), + (self.arrow_heads_att, u'\u25b6')] else: - arrowhead = u'\u2514\u25b6' + arrowhead = [(self.arrow_bars_att, u'\u2514'), + (self.arrow_heads_att, u'\u25b6')] cols.append(('fixed', 2, urwid.Text(arrowhead))) cols.append(self.sumw) line = urwid.Columns(cols, box_columns=bc) @@ -605,6 +615,7 @@ class MessageWidget(urwid.WidgetWrap): prefixchars.append(('fixed', 1, urwid.SolidFill(c))) spacer = urwid.Columns(prefixchars, box_columns=range(length)) + spacer = urwid.AttrMap(spacer, self.arrow_bars_att) return ('fixed', length, spacer) def _get_arrowhead_aligner(self): @@ -612,7 +623,8 @@ class MessageWidget(urwid.WidgetWrap): aligner = u'\u2502' else: aligner = ' ' - return ('fixed', 1, urwid.SolidFill(aligner)) + aligner = urwid.SolidFill(aligner) + return ('fixed', 1, urwid.AttrMap(aligner, self.arrow_bars_att)) def selectable(self): return True diff --git a/extra/theme_convert.py b/extra/theme_convert.py index 0873bd02..8a0d26ca 100755 --- a/extra/theme_convert.py +++ b/extra/theme_convert.py @@ -103,6 +103,8 @@ if __name__ == "__main__": new['thread']['attachment'] = lookup(['thread','attachment']) new['thread']['attachment_focus'] = lookup(['thread','attachment_focus']) new['thread']['body'] = lookup(['thread','body']) + new['thread']['arrow_heads'] = lookup(['thread','body']) + new['thread']['arrow_bars'] = lookup(['thread','body']) new['thread']['header'] = lookup(['thread','header']) new['thread']['header_key'] = lookup(['thread','header_key']) new['thread']['header_value'] = lookup(['thread','header_value']) -- cgit v1.2.3 From 1319f79bfe441b355ea35404198d53f079088a7c Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Fri, 20 Jul 2012 22:16:31 +0100 Subject: fix small annoyances --- alot/widgets.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/alot/widgets.py b/alot/widgets.py index 74b30e32..0145bb33 100644 --- a/alot/widgets.py +++ b/alot/widgets.py @@ -155,13 +155,13 @@ class ThreadlineWidget(urwid.AttrMap): elif name == 'subject': if self.thread: - subjectstring = self.thread.get_subject() or '' + subjectstring = self.thread.get_subject() or ' ' else: - subjectstring = '' + subjectstring = ' ' # sanitize subject string: subjectstring = subjectstring.replace('\n', ' ') subjectstring = subjectstring.replace('\r', '') - subjectstring = pad(subjectstring.strip()) + subjectstring = pad(subjectstring) subject_w = AttrFlipWidget(urwid.Text(subjectstring, wrap='clip'), struct['subject']) -- cgit v1.2.3 From c7d5f14c01957e8aa4d3e0a9da1082280598c5e1 Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Sat, 21 Jul 2012 00:23:39 +0100 Subject: better default theme --- alot/defaults/default.theme | 65 +++++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/alot/defaults/default.theme b/alot/defaults/default.theme index cdfe8665..62ed4433 100644 --- a/alot/defaults/default.theme +++ b/alot/defaults/default.theme @@ -1,26 +1,26 @@ [global] - footer = 'standout','','light green,bold','dark blue','light green,bold','dark blue' - body = 'default','','default','default','dark red','default' + footer = 'standout','','white,bold','dark blue','white,bold','#006' + body = 'default','','dark gray','default','g58','default' notify_error = 'standout','','white','dark red','white','dark red' notify_normal = 'default','','light gray','dark gray','light gray','#68a' - prompt = 'default','','light gray','black','light gray','black' - tag = 'default','','brown','black','brown','default' + prompt = 'default','','light gray','black','light gray','g11' + tag = 'default','','light gray','black','light gray','default' tag_focus = 'standout, bold','','white','dark gray','#ffa','#68a' [help] text = 'default','','default','dark gray','default','g35' section = 'underline','','bold,underline','dark gray','bold,underline','g35' title = 'standout','','white','dark blue','white,bold,underline','g35' [bufferlist] - line_focus = 'standout','','white','dark gray','#ffa','#68a' + line_focus = 'standout','','yellow','light gray','#ff8','g58' line_even = 'default','','light gray','black','default','g3' line_odd = 'default','','light gray','black','default','default' [taglist] - line_focus = 'standout','','white','dark gray','#ffa','#68a' + line_focus = 'standout','','yellow','light gray','#ff8','g58' line_even = 'default','','light gray','black','default','g3' line_odd = 'default','','light gray','black','default','default' [thread] - arrow_heads = '','','light red','','light red','' - arrow_bars = '','','dark red','','dark red','' + arrow_heads = '','','dark red','','#a00','' + arrow_bars = '','','dark red','','#800','' attachment = 'default','','light gray','dark gray','light gray','dark gray' attachment_focus = 'underline','','light gray','light green','light gray','light green' body = 'default','','light gray','default','light gray','default' @@ -29,9 +29,9 @@ header_value = 'default','','light gray','dark gray','light gray','dark gray' [[summary]] - even = 'default','','white','light blue','white','#068' - odd = 'default','','white','dark blue','white','#006' - focus = 'standout','','white','dark cyan','#ff8','g58' + even = 'default','','white','light blue','white','#006' + odd = 'default','','white','dark blue','white','#068' + focus = 'standout','','white','light gray','#ff8','g58' [envelope] body = 'default','','light gray','default','light gray','default' @@ -41,45 +41,46 @@ [search] [[threadline]] normal = 'default','','default','default','#6d6','default' - focus = 'standout','','light gray','dark gray','white','#68a' + focus = 'standout','','light gray','light gray','g85','g58' parts = date,mailcount,tags,authors,subject [[[date]]] - normal = 'default','','light gray','default','g58','default' - focus = 'standout','','light gray','dark gray','g89','#68a' + normal = 'default','','light gray','default','g74','default' + focus = 'standout','','yellow','light gray','yellow','g58' width = 'fit',10,10 [[[mailcount]]] - normal = 'default','','light gray','default','light gray','default' - focus = 'standout','','light gray','dark gray','g89','#68a' + normal = 'default','','light gray','default','g66','default' + focus = 'standout','','yellow','light gray','yellow','g58' width = 'fit', 5,5 [[[tags]]] - normal = 'bold','','brown','default','#a86','default' - focus = 'standout','','yellow,bold','dark gray','#ff8','#68a' + normal = 'bold','','dark cyan','','dark cyan','' + focus = 'standout','','yellow','light gray','yellow','g58' [[[authors]]] - normal = 'default,underline','','dark green','default','#6d6','default' - focus = 'standout','','dark green,bold','dark gray','#8f6','#68a' + normal = 'default,underline','','light blue','default','#068','default' + focus = 'standout','','yellow','light gray','yellow','g58' width = 'fit',0,30 [[[subject]]] - normal = 'default','','light gray','default','g58','default' - focus = 'standout','','light gray','dark gray','g89','#68a' - width = 'weight', 1 + normal = 'default','','light gray','default','g66','default' + focus = 'standout','','yellow','light gray','yellow','g58' + #width = 'weight', 1 [[[content]]] - normal = 'default','','dark gray','default','#866','default' - focus = 'standout','','black','dark gray','#866','#68a' + normal = 'default','','light gray','default','dark gray','default' + focus = 'standout','','yellow','light gray','yellow','g58' width = 'weight', 1 # highlight threads containing unread messages [[threadline-unread]] - normal = 'default','','default,bold','default','#6d6,bold','default' tagged_with = 'unread' + normal = 'default','','default,bold','default','#6d6,bold','default' + parts = date,mailcount,tags,authors,subject [[[date]]] - normal = 'default','','light gray,bold','default','g58,bold','default' + normal = 'default','','light gray,bold','default','white','default' [[[mailcount]]] - normal = 'default','','light gray,bold','default','light gray,bold','default' + normal = 'default','','light gray,bold','default','g93','default' [[[tags]]] - normal = 'bold','','brown,bold','default','#a86,bold','default' + normal = 'bold','','dark cyan,bold','','#6dd','' [[[authors]]] - normal = 'default,underline','','dark green,bold','default','#6d6,bold','default' + normal = 'default,underline','','light blue,bold','default','#68f','default' [[[subject]]] - normal = 'default','','light gray,bold','default','g58,bold','default' + normal = 'default','','light gray,bold','default','g93','default' [[[content]]] - normal = 'default','','dark gray,bold','default','#866,bold','default' + normal = 'default','','light gray,bold','default','dark gray,bold','default' -- cgit v1.2.3 From 9a4158ebe38c40d7897c12be9b5257caec5e1293 Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Sat, 21 Jul 2012 16:02:25 +0100 Subject: better validation error reports --- alot/settings/__init__.py | 6 +++++- alot/settings/theme.py | 2 +- alot/settings/utils.py | 20 ++++++++++++++------ 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/alot/settings/__init__.py b/alot/settings/__init__.py index 8205d099..dc729b0e 100644 --- a/alot/settings/__init__.py +++ b/alot/settings/__init__.py @@ -95,7 +95,11 @@ class SettingsManager(object): raise ConfigError(err_msg % (themestring, themes_dir)) else: theme_path = os.path.join(themes_dir, themestring) - self._theme = Theme(theme_path) + try: + self._theme = Theme(theme_path) + except ConfigError as e: + err_msg = 'Theme file %s failed validation:\n' + raise ConfigError((err_msg % themestring) + e.message) self._accounts = self._parse_accounts(self._config) self._accountmap = self._account_table(self._accounts) diff --git a/alot/settings/theme.py b/alot/settings/theme.py index ea154504..78f86fc5 100644 --- a/alot/settings/theme.py +++ b/alot/settings/theme.py @@ -39,7 +39,7 @@ class Theme(object): indefault = set(threadline.sections) diff = listed.difference(here.union(indefault)) if diff: - msg = 'missing threadline parts: %s' % diff + msg = 'missing threadline parts: %s' % ', '.join(diff) raise ConfigError(msg) def get_attribute(self, colourmode, mode, name, part=None): diff --git a/alot/settings/utils.py b/alot/settings/utils.py index accc3ab8..0236cf58 100644 --- a/alot/settings/utils.py +++ b/alot/settings/utils.py @@ -24,21 +24,29 @@ def read_config(configpath=None, specpath=None, checks={}): config = ConfigObj(infile=configpath, configspec=specpath, file_error=True, encoding='UTF8') except (ConfigObjError, IOError), e: - raise ConfigError('Could not read "%s": %s' % (configpath, e)) + raise ConfigError('Couls not read %s' % configpath) + except UnboundLocalError as e: + # this works around a bug in configobj + msg = '%s is malformed. Check for sections without parents..' + raise ConfigError(msg % configpath) if specpath: validator = Validator() validator.functions.update(checks) - results = config.validate(validator) + results = config.validate(validator, preserve_errors=True) if results != True: - error_msg = 'Validation errors occurred:\n' + error_msg = '' for (section_list, key, res) in flatten_errors(config, results): if key is not None: - msg = 'key "%s" in section "%s" failed validation: %s' - msg = msg % (key, ', '.join(section_list), res) + if res == False: + msg = 'key "%s" in section "%s" is missing.' + msg = msg % (key, ', '.join(section_list)) + else: + msg = 'key "%s" in section "%s" failed validation: %s' + msg = msg % (key, ', '.join(section_list), res) else: - msg = 'section "%s" is malformed' % ', '.join(section_list) + msg = 'section "%s" is missing' % '.'.join(section_list) error_msg += msg + '\n' raise ConfigError(error_msg) return config -- cgit v1.2.3 From 3cca629ba712ad3ad6b9f6ac86797248101434ab Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Sat, 21 Jul 2012 16:17:00 +0100 Subject: fix issue with overwriting AttrSpecs --- alot/settings/utils.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/alot/settings/utils.py b/alot/settings/utils.py index 0236cf58..d274fb7c 100644 --- a/alot/settings/utils.py +++ b/alot/settings/utils.py @@ -4,6 +4,7 @@ from configobj import ConfigObj, ConfigObjError, flatten_errors from validate import Validator from errors import ConfigError +from urwid import AttrSpec def read_config(configpath=None, specpath=None, checks={}): @@ -57,7 +58,11 @@ def resolve_att(a, fallback): if a is None: return fallback if a.background in ['default', '']: - a.background = fallback.background + bg = fallback.background + else: + bg = a.background if a.foreground in ['default', '']: - a.foreground = fallback.foreground - return a + fg = fallback.foreground + else: + fg = a.foreground + return AttrSpec(fg, bg) -- cgit v1.2.3 From 5e0741e33a5565ba7a3bdc01813f80cb4a24c0f8 Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Sat, 21 Jul 2012 16:23:04 +0100 Subject: pep8 and pyflakes fixes --- alot/addressbooks.py | 3 ++- alot/errors.py | 2 ++ alot/helper.py | 2 +- alot/settings/__init__.py | 2 -- alot/settings/checks.py | 1 - alot/settings/errors.py | 2 ++ alot/settings/utils.py | 4 ++-- alot/ui.py | 1 - alot/widgets.py | 4 ++-- 9 files changed, 11 insertions(+), 10 deletions(-) diff --git a/alot/addressbooks.py b/alot/addressbooks.py index 00fe9fe7..a8453b09 100644 --- a/alot/addressbooks.py +++ b/alot/addressbooks.py @@ -50,7 +50,8 @@ class AbookAddressBook(AddressBook): res = [] for id in c.sections: for email in c[id]['email']: - if email: res.append((c[id]['name'], email)) + if email: + res.append((c[id]['name'], email)) return res diff --git a/alot/errors.py b/alot/errors.py index 00336fc8..881acf1f 100644 --- a/alot/errors.py +++ b/alot/errors.py @@ -1,6 +1,8 @@ # 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 + + class GPGProblem(Exception): """GPG Error""" pass diff --git a/alot/helper.py b/alot/helper.py index f4ec07cd..78744edf 100644 --- a/alot/helper.py +++ b/alot/helper.py @@ -22,7 +22,6 @@ from twisted.internet.protocol import ProcessProtocol from twisted.internet.defer import Deferred import StringIO import logging -import tempfile def split_commandstring(cmdstring): @@ -35,6 +34,7 @@ def split_commandstring(cmdstring): cmdstring = cmdstring.encode('utf-8', errors='ignore') return shlex.split(cmdstring) + def safely_get(clb, E, on_error=''): """ returns result of :func:`clb` and falls back to `on_error` diff --git a/alot/settings/__init__.py b/alot/settings/__init__.py index dc729b0e..0edfc24c 100644 --- a/alot/settings/__init__.py +++ b/alot/settings/__init__.py @@ -7,9 +7,7 @@ import re import errno import mailcap import logging -import urwid import shutil -from urwid import AttrSpecError from configobj import ConfigObj, Section from alot.account import SendmailAccount diff --git a/alot/settings/checks.py b/alot/settings/checks.py index ca412b68..76d22780 100644 --- a/alot/settings/checks.py +++ b/alot/settings/checks.py @@ -24,7 +24,6 @@ def attr_triple(value): :raises: VdtValueTooLongError, VdtTypeError :rtype: triple of `urwid.AttrSpec` """ - fg = bg = 'default' keys = ['dfg', 'dbg', '1fg', '1bg', '16fg', '16bg', '256fg', '256bg'] acc = {} if not isinstance(value, (list, tuple)): diff --git a/alot/settings/errors.py b/alot/settings/errors.py index 9095e897..606f78e1 100644 --- a/alot/settings/errors.py +++ b/alot/settings/errors.py @@ -1,6 +1,8 @@ # 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 + + class ConfigError(Exception): """could not parse user config""" pass diff --git a/alot/settings/utils.py b/alot/settings/utils.py index d274fb7c..f6cc3613 100644 --- a/alot/settings/utils.py +++ b/alot/settings/utils.py @@ -24,9 +24,9 @@ def read_config(configpath=None, specpath=None, checks={}): try: config = ConfigObj(infile=configpath, configspec=specpath, file_error=True, encoding='UTF8') - except (ConfigObjError, IOError), e: + except (ConfigObjError, IOError): raise ConfigError('Couls not read %s' % configpath) - except UnboundLocalError as e: + except UnboundLocalError: # this works around a bug in configobj msg = '%s is malformed. Check for sections without parents..' raise ConfigError(msg % configpath) diff --git a/alot/ui.py b/alot/ui.py index 9e5e81b0..92262e34 100644 --- a/alot/ui.py +++ b/alot/ui.py @@ -4,7 +4,6 @@ import urwid import logging from twisted.internet import reactor, defer -import sys from settings import settings from buffers import BufferlistBuffer diff --git a/alot/widgets.py b/alot/widgets.py index 0145bb33..5edefbef 100644 --- a/alot/widgets.py +++ b/alot/widgets.py @@ -12,7 +12,6 @@ import alot.db.message as message from alot.db.attachment import Attachment import time from alot.db.utils import decode_header -from alot.settings.utils import resolve_att class AttrFlipWidget(urwid.AttrMap): @@ -664,7 +663,8 @@ class MessageSummaryWidget(urwid.WidgetWrap): attr = settings.get_theming_attribute('thread', 'summary', 'even') else: attr = settings.get_theming_attribute('thread', 'summary', 'odd') - focus_att = settings.get_theming_attribute('thread', 'summary', 'focus') + focus_att = settings.get_theming_attribute('thread', 'summary', + 'focus') cols = [] sumstr = self.__str__() -- cgit v1.2.3 From edd1849f00e9f08366a743e8cdca3ebf5482039e Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Sat, 21 Jul 2012 16:26:04 +0100 Subject: clean up widget docstrings --- alot/widgets.py | 32 ++------------------------------ 1 file changed, 2 insertions(+), 30 deletions(-) diff --git a/alot/widgets.py b/alot/widgets.py index 5edefbef..c09bda0d 100644 --- a/alot/widgets.py +++ b/alot/widgets.py @@ -73,17 +73,6 @@ class ThreadlineWidget(urwid.AttrMap): """ selectable line widget that represents a :class:`~alot.db.Thread` in the :class:`~alot.buffers.SearchBuffer`. - - Respected settings: - * `general.display_content_in_threadline` - * `general.timestamp_format` - Theme settings: - * `search_thread, search_thread_focus` - * `search_thread_date, search_thread_date_focus` - * `search_thread_mailcount, search_thread_mailcount_focus` - * `search_thread_authors, search_thread_authors_focus` - * `search_thread_subject, search_thread_subject_focus` - * `search_thread_content, search_thread_content_focus` """ # The pretty_datetime needs 9 characters, but only 8 if locale # doesn't use am/pm (in which case "jan 2012" is the longest) @@ -283,10 +272,8 @@ class TagWidget(urwid.AttrMap): """ text widget that renders a tagstring. - It looks up the string it displays in the `tag-translate` section - of the config as well as custom theme settings for its tag. The - tag may also be configured as hidden, which users of this widget - should honour. + It looks up the string it displays in the `tags` section + of the config as well as custom theme settings for its tag. """ def __init__(self, tag, fallback_normal=None, fallback_focus=None): self.tag = tag @@ -418,9 +405,6 @@ class CompleteEdit(urwid.Edit): class MessageWidget(urwid.WidgetWrap): """ Flow widget that renders a :class:`~alot.db.message.Message`. - - Respected settings: - * `general.displayed_headers` """ #TODO: atm this is heavily bent to work nicely with ThreadBuffer to display #a tree structure. A better way would be to keep this widget simple @@ -643,11 +627,6 @@ class MessageWidget(urwid.WidgetWrap): class MessageSummaryWidget(urwid.WidgetWrap): """ one line summary of a :class:`~alot.db.message.Message`. - - Theme settings: - * `thread_summary_even` - * `thread_summary_odd` - * `thread_summary_focus` """ def __init__(self, message, even=True): @@ -731,9 +710,6 @@ class HeadersList(urwid.WidgetWrap): class MessageBodyWidget(urwid.AttrMap): """ displays printable parts of an email - - Theme settings: - * `thread_body` """ def __init__(self, msg): @@ -745,10 +721,6 @@ class MessageBodyWidget(urwid.AttrMap): class AttachmentWidget(urwid.WidgetWrap): """ one-line summary of an :class:`~alot.db.attachment.Attachment`. - - Theme settings: - * `thread_attachment` - * `thread_attachment_focus` """ def __init__(self, attachment, selectable=True): self._selectable = selectable -- cgit v1.2.3 From 91156fe76ea1048c0d3167008f2cbf6e19d4fcfc Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Sat, 21 Jul 2012 16:42:31 +0100 Subject: docs: reference to attribute doc --- docs/source/configuration/index.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/source/configuration/index.rst b/docs/source/configuration/index.rst index ba15eb42..a8e23eae 100644 --- a/docs/source/configuration/index.rst +++ b/docs/source/configuration/index.rst @@ -283,10 +283,10 @@ Theme Files ----------- contain a section for each :ref:`MODE ` plus "help" for the bindings-help overlay and "global" for globally used themables like footer, prompt etc. -Each such section contains attribute values for the parts that can be themed. -The names of the themables should be self-explanatory. -Have a look at the default theme file at :file:`alot/defaults/default.theme` -and the config spec :file:`alot/defaults/default.theme` for the exact format. +Each such section defines colour :ref:`attributes ` for the parts that +can be themed. The names of the themables should be self-explanatory. +Have a look at the default theme file at :file:`alot/defaults/default.theme` and the config spec +:file:`alot/defaults/default.theme` for the exact format. .. _config.theming.attributes: -- cgit v1.2.3 From 45db4b1ae2c3f95164db644fb1732569df798a52 Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Sat, 21 Jul 2012 16:44:44 +0100 Subject: make left align default, fix date padding --- alot/defaults/default.theme | 1 + alot/defaults/theme.spec | 2 +- alot/widgets.py | 6 ------ 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/alot/defaults/default.theme b/alot/defaults/default.theme index 62ed4433..e88f4a97 100644 --- a/alot/defaults/default.theme +++ b/alot/defaults/default.theme @@ -47,6 +47,7 @@ normal = 'default','','light gray','default','g74','default' focus = 'standout','','yellow','light gray','yellow','g58' width = 'fit',10,10 + alignment = right [[[mailcount]]] normal = 'default','','light gray','default','g66','default' focus = 'standout','','yellow','light gray','yellow','g58' diff --git a/alot/defaults/theme.spec b/alot/defaults/theme.spec index 176d7b8a..05d59c16 100644 --- a/alot/defaults/theme.spec +++ b/alot/defaults/theme.spec @@ -33,7 +33,7 @@ normal = attrtriple focus = attrtriple width = widthtuple(default=None) - alignment = align(default='right') + alignment = align(default='left') [[__many__]] normal = attrtriple(default=None) focus = attrtriple(default=None) diff --git a/alot/widgets.py b/alot/widgets.py index c09bda0d..16d4b53b 100644 --- a/alot/widgets.py +++ b/alot/widgets.py @@ -10,7 +10,6 @@ from alot.helper import tag_cmp from alot.helper import string_decode import alot.db.message as message from alot.db.attachment import Attachment -import time from alot.db.utils import decode_header @@ -74,10 +73,6 @@ class ThreadlineWidget(urwid.AttrMap): selectable line widget that represents a :class:`~alot.db.Thread` in the :class:`~alot.buffers.SearchBuffer`. """ - # The pretty_datetime needs 9 characters, but only 8 if locale - # doesn't use am/pm (in which case "jan 2012" is the longest) - pretty_datetime_len = 8 if len(time.strftime("%P")) == 0 else 9 - def __init__(self, tid, dbman): self.dbman = dbman self.thread = dbman.get_thread(tid) @@ -115,7 +110,6 @@ class ThreadlineWidget(urwid.AttrMap): if self.thread: newest = self.thread.get_newest_date() datestring = settings.represent_datetime(newest) - datestring = datestring.rjust(self.pretty_datetime_len) datestring = pad(datestring) width = len(datestring) part = AttrFlipWidget(urwid.Text(datestring), struct['date']) -- cgit v1.2.3 From 5399ad7385657f3a32e978e5f79e656533bc47be Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Sat, 21 Jul 2012 17:10:31 +0100 Subject: update extra themes --- extra/themes/mutt | 94 +++++++++++++++++++++++++++++++++++++++++++++ extra/themes/solarized | 37 ++++++++++-------- extra/themes/solarized_dark | 34 +++++++++------- 3 files changed, 137 insertions(+), 28 deletions(-) create mode 100644 extra/themes/mutt diff --git a/extra/themes/mutt b/extra/themes/mutt new file mode 100644 index 00000000..42fcd7f1 --- /dev/null +++ b/extra/themes/mutt @@ -0,0 +1,94 @@ +############################################################################### +# MUTT +# +# colour theme for alot. © 2012 Patrick Totzke, GNU GPL3+ +# https://github.com/pazz/alot +############################################################################### + +[global] + footer = 'standout,bold','','light green,bold','dark blue','light green,bold','dark blue' + body = '','','','black','','black' + notify_error = 'standout','','white','dark red','white','dark red' + notify_normal = '','','light gray','black','light gray','#68a' + prompt = '','','light gray','black','light gray','black' + tag = '','','yellow','','yellow','' + tag_focus = 'standout, bold','','yellow','','yellow','' +[help] + text = '','','','dark gray','','dark gray' + section = 'underline','','white,underline','dark gray','white,underline','dark gray' + title = 'standout','','white,underline','dark gray','white,underline','dark gray' +[bufferlist] + line_even = '','','light gray','black','light gray','black' + line_odd = '','','light gray','black','light gray','black' + line_focus = 'standout','','black','dark cyan','black','dark cyan' +[taglist] + line_even = '','','light gray','black','light gray','black' + line_odd = '','','light gray','black','light gray','black' + line_focus = 'standout','','black','dark cyan','black','dark cyan' +[thread] + arrow_heads = '','','dark red','black','dark red','black' + arrow_bars = '','','dark red','black','dark red','black' + attachment = '','','yellow,bold','black','yellow,bold','black' + attachment_focus = 'standout','','black','yellow','black','yellow' + body = '','','light gray','black','light gray','black' + header = '','','dark cyan','black','dark cyan','black' + header_key = '','','dark cyan','black','dark cyan','black' + header_value = '','','dark cyan','black','dark cyan','black' + + [[summary]] + even = '','','light gray','black','light gray','black' + odd = '','','light gray','black','light gray','black' + focus = 'standout','','black','dark cyan','black','dark cyan' + +[envelope] + body = '','','light gray','black','light gray','black' + header = '','','dark cyan','black','dark cyan','black' + header_key = '','','dark cyan','black','dark cyan','black' + header_value = '','','dark cyan','black','dark cyan','black' +[search] + [[threadline]] + normal = '','','','black','','black' + focus = 'standout','','black','dark cyan','black','dark cyan' + parts = date,authors,mailcount,subject,tags + [[[date]]] + normal = '','','','black','','black' + focus = 'standout','','black','dark cyan','black','dark cyan' + width = 'fit',10,10 + alignment = right + [[[mailcount]]] + normal = '','','','black','','black' + focus = 'standout','','black','dark cyan','black','dark cyan' + width = 'fit', 5,5 + [[[tags]]] + normal = '','','','black','','black' + focus = 'standout','','black','dark cyan','black','dark cyan' + [[[authors]]] + normal = '','','','black','','black' + focus = 'standout','','black','dark cyan','black','dark cyan' + width = 'fit',25,25 + [[[subject]]] + normal = '','','','black','','black' + focus = 'standout','','black','dark cyan','black','dark cyan' + width = 'weight', 1 + [[[content]]] + normal = '','','','black','','black' + focus = 'standout','','black','dark cyan','black','dark cyan' + width = 'weight', 1 + + # highlight threads containing unread messages +# [[threadline-unread]] +# tagged_with = 'unread' +# [[[date]]] +# normal = 'bold','','light gray','','light gray','' +# alignment = left +# [[[mailcount]]] +# normal = 'bold','','light gray','','light gray','' +# [[[tags]]] +# normal = 'bold','','light gray','','light green,bold','' +# [[[authors]]] +# normal = 'bold','','light gray','','light gray','' +# alignment = left +# [[[subject]]] +# normal = 'bold','','light gray','','light gray','' +# [[[content]]] +# normal = 'bold','','light gray','','light gray','' diff --git a/extra/themes/solarized b/extra/themes/solarized index 7be7d1c6..11030e1c 100644 --- a/extra/themes/solarized +++ b/extra/themes/solarized @@ -1,5 +1,5 @@ ############################################################################### -# SOLARIZED +# SOLARIZED DARK # # colour theme for alot. © 2012 Patrick Totzke, GNU GPL3+ # http://ethanschoonover.com/solarized @@ -45,34 +45,40 @@ 256_cyan = '#088' 256_green = 'dark green' - # This is the actual alot theme [global] - footer = 'standout','default','%(16_base00)s','%(16_base3)s','%(256_base00)s','%(256_base3)s' + footer = 'standout','default','%(16_base01)s','%(16_base2)s','%(256_base01)s','%(256_base2)s' body = 'default','default','%(16_base00)s','%(16_base3)s','%(256_base00)s','%(256_base3)s' notify_error = 'standout','default','%(16_base3)s','%(16_red)s','%(256_base3)s','%(256_red)s' notify_normal = 'default','default','%(16_base00)s','%(16_base2)s','%(256_base00)s','%(256_base2)s' prompt = 'default','default','%(16_base00)s','%(16_base2)s','%(256_base00)s','%(256_base2)s' tag = 'default','default','%(16_yellow)s','%(16_base3)s','%(256_yellow)s','%(256_base3)s' - tag_focus = 'default','default','%(16_base2)s','%(16_yellow)s','%(256_base2)s','%(256_yellow)s' + tag_focus = 'standout','default','%(16_base3)s','%(16_yellow)s','%(256_base3)s','%(256_yellow)s' [help] text = 'default','default','light gray','dark gray','%(256_base00)s','%(256_base3)s' section = 'underline','default','%(16_base01)s','%(16_base2)s','%(256_base01)s','%(256_base2)s' title = 'standout','default','%(16_base01)s','%(16_base3)s','%(256_base01)s','%(256_base3)s' +[taglist] + line_focus = 'standout','default','%(16_base2)s','%(16_yellow)s','%(256_base2)s','%(256_yellow)s' + line_even = 'default','default','%(16_base00)s','%(16_base3)s','%(256_base00)s','%(256_base3)s' + line_odd = 'default','default','%(16_base00)s','%(16_base2)s','%(256_base00)s','%(256_base2)s' [bufferlist] - focus = 'standout','default','%(16_base2)s','%(16_yellow)s','%(256_base2)s','%(256_yellow)s' - results_even = 'default','default','%(16_base00)s','%(16_base3)s','%(256_base00)s','%(256_base3)s' - results_odd = 'default','default','%(16_base00)s','%(16_base2)s','%(256_base00)s','%(256_base2)s' + line_focus = 'standout','default','%(16_base2)s','%(16_yellow)s','%(256_base2)s','%(256_yellow)s' + line_even = 'default','default','%(16_base00)s','%(16_base3)s','%(256_base00)s','%(256_base3)s' + line_odd = 'default','default','%(16_base00)s','%(16_base2)s','%(256_base00)s','%(256_base2)s' [thread] attachment = 'default','default','%(16_base00)s','%(16_base3)s','%(256_base00)s','%(256_base3)s' attachment_focus = 'underline','default','%(16_base2)s','%(16_yellow)s','%(256_base2)s','%(256_yellow)s' body = 'default','default','%(16_base00)s','%(16_base3)s','%(256_base00)s','%(256_base3)s' + arrow_bars = 'default','default','%(16_yellow)s','%(16_base3)s','%(256_yellow)s','%(256_base3)s' + arrow_heads = 'default','default','%(16_yellow)s','%(16_base3)s','%(256_yellow)s','%(256_base3)s' header = 'default','default','%(16_base00)s','%(16_base2)s','%(256_base00)s','%(256_base2)s' header_key = 'default','default','%(16_magenta)s','%(16_base2)s','%(256_magenta)s','%(256_base2)s' header_value = 'default','default','%(16_blue)s','%(16_base2)s','%(256_blue)s','%(256_base2)s' - summary_even = 'default','default','%(16_base00)s','%(16_base2)s','%(256_base00)s','%(256_base2)s' - summary_focus = 'standout','default','%(16_base2)s','%(16_yellow)s','%(256_base2)s','%(256_yellow)s' - summary_odd = 'default','default','%(16_base00)s','%(16_base3)s','%(256_base00)s','%(256_base3)s' + [[summary]] + even = 'default','default','%(16_base00)s','%(16_base2)s','%(256_base00)s','%(256_base2)s' + focus = 'standout','default','%(16_base3)s','%(16_yellow)s','%(256_base3)s','%(256_yellow)s' + odd = 'default','default','%(16_base00)s','%(16_base3)s','%(256_base00)s','%(256_base3)s' [envelope] body = 'default','default','%(16_base00)s','%(16_base3)s','%(256_base00)s','%(256_base3)s' header = 'default','default','%(16_base00)s','%(16_base2)s','%(256_base00)s','%(256_base2)s' @@ -85,19 +91,20 @@ parts = date,mailcount,tags,authors,subject [[[date]]] normal = 'default','default','%(16_base01)s','%(16_base3)s','%(256_base01)s','%(256_base3)s' - focus = 'standout','default','%(16_base2)s','%(16_yellow)s','%(256_base2)s','%(256_yellow)s' + focus = 'standout','default','%(16_base3)s','%(16_yellow)s','%(256_base3)s','%(256_yellow)s' [[[mailcount]]] normal = 'default','default','%(16_base01)s','%(16_base3)s','%(256_base01)s','%(256_base3)s' - focus = 'standout','default','%(16_base1)s','%(16_yellow)s','%(256_base1)s','%(256_yellow)s' + focus = 'standout','default','%(16_base2)s','%(16_yellow)s','%(256_base2)s','%(256_yellow)s' [[[tags]]] normal = 'bold','default','%(16_yellow)s','%(16_base3)s','%(256_yellow)s','%(256_base3)s' - focus = 'standout','default','%(16_base2)s','%(16_orange)s','%(256_base2)s','%(256_orange)s' + focus = 'standout','default','%(16_base3)s','%(16_yellow)s','%(256_base3)s','%(256_yellow)s' [[[authors]]] normal = 'default,underline','default','%(16_cyan)s','%(16_base3)s','%(256_cyan)s','%(256_base3)s' - focus = 'standout','default','%(16_base2)s,bold','%(16_yellow)s','%(256_base2)s,bold','%(256_yellow)s' + focus = 'standout','default','%(16_base2)s','%(16_yellow)s','%(256_base2)s','%(256_yellow)s' + width = 'fit',0,30 [[[subject]]] normal = 'default','default','%(16_base00)s','%(16_base3)s','%(256_base00)s','%(256_base3)s' - focus = 'standout','default','%(16_base1)s','%(16_yellow)s','%(256_base1)s','%(256_yellow)s' + focus = 'standout','default','%(16_base3)s','%(16_yellow)s','%(256_base3)s','%(256_yellow)s' width = 'weight',1 [[[content]]] normal = 'default','default','%(16_base1)s','%(16_base3)s','%(256_base1)s','%(256_base3)s' diff --git a/extra/themes/solarized_dark b/extra/themes/solarized_dark index 351b2045..ce414836 100644 --- a/extra/themes/solarized_dark +++ b/extra/themes/solarized_dark @@ -48,31 +48,38 @@ # This is the actual alot theme [global] - footer = 'standout','default','%(16_base0)s','%(16_base03)s','%(256_base0)s','%(256_base03)s' + footer = 'standout','default','%(16_base1)s','%(16_base02)s','%(256_base1)s','%(256_base02)s' body = 'default','default','%(16_base0)s','%(16_base03)s','%(256_base0)s','%(256_base03)s' notify_error = 'standout','default','%(16_base3)s','%(16_red)s','%(256_base3)s','%(256_red)s' notify_normal = 'default','default','%(16_base01)s','%(16_base02)s','%(256_base01)s','%(256_base02)s' prompt = 'default','default','%(16_base0)s','%(16_base02)s','%(256_base0)s','%(256_base02)s' tag = 'default','default','%(16_yellow)s','%(16_base03)s','%(256_yellow)s','%(256_base03)s' - tag_focus = 'default','default','%(16_base02)s','%(16_yellow)s','%(256_base02)s','%(256_yellow)s' + tag_focus = 'standout','default','%(16_base03)s','%(16_yellow)s','%(256_base03)s','%(256_yellow)s' [help] text = 'default','default','light gray','dark gray','%(256_base0)s','%(256_base03)s' section = 'underline','default','%(16_base1)s','%(16_base02)s','%(256_base1)s','%(256_base02)s' title = 'standout','default','%(16_base1)s','%(16_base03)s','%(256_base1)s','%(256_base03)s' +[taglist] + line_focus = 'standout','default','%(16_base02)s','%(16_yellow)s','%(256_base02)s','%(256_yellow)s' + line_even = 'default','default','%(16_base0)s','%(16_base03)s','%(256_base0)s','%(256_base03)s' + line_odd = 'default','default','%(16_base0)s','%(16_base02)s','%(256_base0)s','%(256_base02)s' [bufferlist] - focus = 'standout','default','%(16_base02)s','%(16_yellow)s','%(256_base02)s','%(256_yellow)s' - results_even = 'default','default','%(16_base0)s','%(16_base03)s','%(256_base0)s','%(256_base03)s' - results_odd = 'default','default','%(16_base0)s','%(16_base02)s','%(256_base0)s','%(256_base02)s' + line_focus = 'standout','default','%(16_base02)s','%(16_yellow)s','%(256_base02)s','%(256_yellow)s' + line_even = 'default','default','%(16_base0)s','%(16_base03)s','%(256_base0)s','%(256_base03)s' + line_odd = 'default','default','%(16_base0)s','%(16_base02)s','%(256_base0)s','%(256_base02)s' [thread] attachment = 'default','default','%(16_base0)s','%(16_base03)s','%(256_base0)s','%(256_base03)s' attachment_focus = 'underline','default','%(16_base02)s','%(16_yellow)s','%(256_base02)s','%(256_yellow)s' + arrow_bars = 'default','default','%(16_yellow)s','%(16_base03)s','%(256_yellow)s','%(256_base03)s' + arrow_heads = 'default','default','%(16_yellow)s','%(16_base03)s','%(256_yellow)s','%(256_base03)s' body = 'default','default','%(16_base0)s','%(16_base03)s','%(256_base0)s','%(256_base03)s' header = 'default','default','%(16_base0)s','%(16_base02)s','%(256_base0)s','%(256_base02)s' header_key = 'default','default','%(16_magenta)s','%(16_base02)s','%(256_magenta)s','%(256_base02)s' header_value = 'default','default','%(16_blue)s','%(16_base02)s','%(256_blue)s','%(256_base02)s' - summary_even = 'default','default','%(16_base0)s','%(16_base02)s','%(256_base0)s','%(256_base02)s' - summary_focus = 'standout','default','%(16_base02)s','%(16_yellow)s','%(256_base02)s','%(256_yellow)s' - summary_odd = 'default','default','%(16_base0)s','%(16_base03)s','%(256_base0)s','%(256_base03)s' + [[summary]] + even = 'default','default','%(16_base0)s','%(16_base02)s','%(256_base0)s','%(256_base02)s' + focus = 'standout','default','%(16_base03)s','%(16_yellow)s','%(256_base03)s','%(256_yellow)s' + odd = 'default','default','%(16_base0)s','%(16_base03)s','%(256_base0)s','%(256_base03)s' [envelope] body = 'default','default','%(16_base0)s','%(16_base03)s','%(256_base0)s','%(256_base03)s' header = 'default','default','%(16_base0)s','%(16_base02)s','%(256_base0)s','%(256_base02)s' @@ -85,19 +92,20 @@ parts = date,mailcount,tags,authors,subject [[[date]]] normal = 'default','default','%(16_base1)s','%(16_base03)s','%(256_base1)s','%(256_base03)s' - focus = 'standout','default','%(16_base02)s','%(16_yellow)s','%(256_base02)s','%(256_yellow)s' + focus = 'standout','default','%(16_base02)s,bold','%(16_yellow)s','%(256_base02)s,bold','%(256_yellow)s' [[[mailcount]]] normal = 'default','default','%(16_base1)s','%(16_base03)s','%(256_base1)s','%(256_base03)s' - focus = 'standout','default','%(16_base01)s','%(16_yellow)s','%(256_base01)s','%(256_yellow)s' + focus = 'standout','default','%(16_base02)s','%(16_yellow)s','%(256_base02)s','%(256_yellow)s' [[[tags]]] normal = 'bold','default','%(16_yellow)s','%(16_base03)s','%(256_yellow)s','%(256_base03)s' - focus = 'standout','default','%(16_base02)s','%(16_orange)s','%(256_base02)s','%(256_orange)s' + focus = 'standout','default','%(16_base02)s,bold','%(16_yellow)s','%(256_base02)s,bold','%(256_yellow)s' [[[authors]]] normal = 'default,underline','default','%(16_cyan)s','%(16_base03)s','%(256_cyan)s','%(256_base03)s' - focus = 'standout','default','%(16_base02)s,bold','%(16_yellow)s','%(256_base02)s,bold','%(256_yellow)s' + focus = 'standout','default','%(16_base02)s','%(16_yellow)s','%(256_base02)s','%(256_yellow)s' + width = 'fit',0,30 [[[subject]]] normal = 'default','default','%(16_base0)s','%(16_base03)s','%(256_base0)s','%(256_base03)s' - focus = 'standout','default','%(16_base01)s','%(16_yellow)s','%(256_base01)s','%(256_yellow)s' + focus = 'standout','default','%(16_base02)s,bold','%(16_yellow)s','%(256_base02)s,bold','%(256_yellow)s' width = 'weight',1 [[[content]]] normal = 'default','default','%(16_base01)s','%(16_base03)s','%(256_base01)s','%(256_base03)s' -- cgit v1.2.3 From 722ce9d49ef9e4e92c5c13aa074aa6b82e43b7bc Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Sat, 21 Jul 2012 17:17:28 +0100 Subject: correctly report interpolation errors --- alot/settings/utils.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/alot/settings/utils.py b/alot/settings/utils.py index f6cc3613..f6912f98 100644 --- a/alot/settings/utils.py +++ b/alot/settings/utils.py @@ -34,7 +34,11 @@ def read_config(configpath=None, specpath=None, checks={}): if specpath: validator = Validator() validator.functions.update(checks) - results = config.validate(validator, preserve_errors=True) + try: + results = config.validate(validator, preserve_errors=True) + except ConfigObjError as e: + raise ConfigError(e.message) + if results != True: error_msg = '' -- cgit v1.2.3 From 3485b3be6c03617f3385a009b66c756c64b06e84 Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Sat, 21 Jul 2012 17:29:56 +0100 Subject: usage info for converter scripts --- extra/tagsections_convert.py | 11 ++++++++++- extra/theme_convert.py | 7 +++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/extra/tagsections_convert.py b/extra/tagsections_convert.py index 82ccd33e..3a73f60f 100755 --- a/extra/tagsections_convert.py +++ b/extra/tagsections_convert.py @@ -1,4 +1,13 @@ #!/usr/bin/python +""" + CONFIG CONVERTER + this script converts your custom tag string section from the v.3.1 syntax + to the current format. + + >>> tagsections_convert.py -o config.new config.old + + will convert your whole alot config safely to the new format. +""" from configobj import ConfigObj import argparse @@ -63,6 +72,6 @@ if __name__ == "__main__": del(sec['bg']) sec['normal'] = att - if sec['hidden']: + if sec.get('hidden'): sec['translated'] = '' cfg.write(out) diff --git a/extra/theme_convert.py b/extra/theme_convert.py index 8a0d26ca..20113be0 100755 --- a/extra/theme_convert.py +++ b/extra/theme_convert.py @@ -1,4 +1,11 @@ #!/usr/bin/python +""" + THEME CONVERTER + this script converts your custom alot theme files from the v.3.1 syntax + to the current format. + + >>> theme_convert.py -o themefile.new themefile.old +""" from configobj import ConfigObj import argparse -- cgit v1.2.3 From d8c106422d7d51f679c5e9c46180ed7f9c8a3d79 Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Sat, 21 Jul 2012 18:13:03 +0100 Subject: added some screenshots --- extra/themes/screenshots/mutt.search.png | Bin 0 -> 163843 bytes extra/themes/screenshots/mutt.thread.png | Bin 0 -> 74014 bytes extra/themes/screenshots/solarized.search.png | Bin 0 -> 221009 bytes extra/themes/screenshots/solarized.thread.png | Bin 0 -> 93938 bytes extra/themes/screenshots/solarized_dark.search.png | Bin 0 -> 194417 bytes extra/themes/screenshots/solarized_dark.thread.png | Bin 0 -> 83319 bytes 6 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 extra/themes/screenshots/mutt.search.png create mode 100644 extra/themes/screenshots/mutt.thread.png create mode 100644 extra/themes/screenshots/solarized.search.png create mode 100644 extra/themes/screenshots/solarized.thread.png create mode 100644 extra/themes/screenshots/solarized_dark.search.png create mode 100644 extra/themes/screenshots/solarized_dark.thread.png diff --git a/extra/themes/screenshots/mutt.search.png b/extra/themes/screenshots/mutt.search.png new file mode 100644 index 00000000..517061e1 Binary files /dev/null and b/extra/themes/screenshots/mutt.search.png differ diff --git a/extra/themes/screenshots/mutt.thread.png b/extra/themes/screenshots/mutt.thread.png new file mode 100644 index 00000000..39b6566c Binary files /dev/null and b/extra/themes/screenshots/mutt.thread.png differ diff --git a/extra/themes/screenshots/solarized.search.png b/extra/themes/screenshots/solarized.search.png new file mode 100644 index 00000000..ea78e9ff Binary files /dev/null and b/extra/themes/screenshots/solarized.search.png differ diff --git a/extra/themes/screenshots/solarized.thread.png b/extra/themes/screenshots/solarized.thread.png new file mode 100644 index 00000000..4a0fe79a Binary files /dev/null and b/extra/themes/screenshots/solarized.thread.png differ diff --git a/extra/themes/screenshots/solarized_dark.search.png b/extra/themes/screenshots/solarized_dark.search.png new file mode 100644 index 00000000..33b71ea2 Binary files /dev/null and b/extra/themes/screenshots/solarized_dark.search.png differ diff --git a/extra/themes/screenshots/solarized_dark.thread.png b/extra/themes/screenshots/solarized_dark.thread.png new file mode 100644 index 00000000..03548cac Binary files /dev/null and b/extra/themes/screenshots/solarized_dark.thread.png differ -- cgit v1.2.3 From 44a7d6f262bd6eb133202d48e9fd176c81d285be Mon Sep 17 00:00:00 2001 From: Patrick Totzke Date: Sat, 21 Jul 2012 18:14:20 +0100 Subject: updated mutt theme --- extra/themes/mutt | 38 ++++++++++---------------------------- 1 file changed, 10 insertions(+), 28 deletions(-) diff --git a/extra/themes/mutt b/extra/themes/mutt index 42fcd7f1..2a6c6b09 100644 --- a/extra/themes/mutt +++ b/extra/themes/mutt @@ -7,14 +7,14 @@ [global] footer = 'standout,bold','','light green,bold','dark blue','light green,bold','dark blue' - body = '','','','black','','black' - notify_error = 'standout','','white','dark red','white','dark red' + body = '','','light gray','black','light gray','black' + notify_error = 'standout','','light gray','dark red','light gray','dark red' notify_normal = '','','light gray','black','light gray','#68a' prompt = '','','light gray','black','light gray','black' tag = '','','yellow','','yellow','' tag_focus = 'standout, bold','','yellow','','yellow','' [help] - text = '','','','dark gray','','dark gray' + text = '','','light gray','dark gray','light gray','dark gray' section = 'underline','','white,underline','dark gray','white,underline','dark gray' title = 'standout','','white,underline','dark gray','white,underline','dark gray' [bufferlist] @@ -47,48 +47,30 @@ header_value = '','','dark cyan','black','dark cyan','black' [search] [[threadline]] - normal = '','','','black','','black' + normal = '','','light gray','black','light gray','black' focus = 'standout','','black','dark cyan','black','dark cyan' parts = date,authors,mailcount,subject,tags [[[date]]] - normal = '','','','black','','black' + normal = '','','light gray','black','light gray','black' focus = 'standout','','black','dark cyan','black','dark cyan' width = 'fit',10,10 alignment = right [[[mailcount]]] - normal = '','','','black','','black' + normal = '','','light gray','black','light gray','black' focus = 'standout','','black','dark cyan','black','dark cyan' width = 'fit', 5,5 [[[tags]]] - normal = '','','','black','','black' + normal = '','','yellow','black','yellow','black' focus = 'standout','','black','dark cyan','black','dark cyan' [[[authors]]] - normal = '','','','black','','black' + normal = '','','light gray','black','light gray','black' focus = 'standout','','black','dark cyan','black','dark cyan' width = 'fit',25,25 [[[subject]]] - normal = '','','','black','','black' + normal = '','','light gray','black','light gray','black' focus = 'standout','','black','dark cyan','black','dark cyan' width = 'weight', 1 [[[content]]] - normal = '','','','black','','black' + normal = '','','light gray','black','light gray','black' focus = 'standout','','black','dark cyan','black','dark cyan' width = 'weight', 1 - - # highlight threads containing unread messages -# [[threadline-unread]] -# tagged_with = 'unread' -# [[[date]]] -# normal = 'bold','','light gray','','light gray','' -# alignment = left -# [[[mailcount]]] -# normal = 'bold','','light gray','','light gray','' -# [[[tags]]] -# normal = 'bold','','light gray','','light green,bold','' -# [[[authors]]] -# normal = 'bold','','light gray','','light gray','' -# alignment = left -# [[[subject]]] -# normal = 'bold','','light gray','','light gray','' -# [[[content]]] -# normal = 'bold','','light gray','','light gray','' -- cgit v1.2.3