"""
This file is part of alot.
Alot is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation, either version 3 of the License, or (at your
option) any later version.
Notmuch is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with notmuch. If not, see .
Copyright (C) 2011 Patrick Totzke
"""
import imp
import os
import mailcap
import codecs
from ConfigParser import SafeConfigParser
DEFAULTS = {
'general': {
'colourmode': '256',
'editor_cmd': "/usr/bin/vim -f -c 'set filetype=mail' +",
'editor_writes_encoding': 'UTF-8',
'terminal_cmd': 'x-terminal-emulator -e',
'spawn_editor': 'False',
'displayed_headers': 'From,To,Cc,Bcc,Subject',
'display_content_in_threadline': 'False',
'authors_maxlength': '30',
'ask_subject': 'True',
'notify_timeout': '2',
'show_statusbar': 'True',
'flush_retry_timeout': '5',
'hooksfile': '~/.alot.py',
'bug_on_exit': 'False',
'timestamp_format': '',
'print_cmd': 'muttprint',
},
'16c-theme': {
'bufferlist_focus_bg': 'dark gray',
'bufferlist_focus_fg': 'white',
'bufferlist_results_even_bg': 'black',
'bufferlist_results_even_fg': 'light gray',
'bufferlist_results_odd_bg': 'black',
'bufferlist_results_odd_fg': 'light gray',
'footer_bg': 'dark blue',
'footer_fg': 'light green',
'header_bg': 'dark blue',
'header_fg': 'white',
'message_attachment_bg': 'dark gray',
'message_attachment_fg': 'light gray',
'message_attachment_focussed_bg': 'light green',
'message_attachment_focussed_fg': 'light gray',
'message_body_bg': 'default',
'message_body_fg': 'light gray',
'message_header_bg': 'dark gray',
'message_header_fg': 'white',
'message_header_key_bg': 'dark gray',
'message_header_key_fg': 'white',
'message_header_value_bg': 'dark gray',
'message_header_value_fg': 'light gray',
'messagesummary_even_bg': 'light blue',
'messagesummary_even_fg': 'white',
'messagesummary_focus_bg': 'dark cyan',
'messagesummary_focus_fg': 'white',
'messagesummary_odd_bg': 'dark blue',
'messagesummary_odd_fg': 'white',
'notify_error_bg': 'dark red',
'notify_error_fg': 'white',
'notify_normal_bg': 'default',
'notify_normal_fg': 'default',
'prompt_bg': 'black',
'prompt_fg': 'light gray',
'tag_focus_bg': 'dark cyan',
'tag_focus_fg': 'white',
'tag_bg': 'black',
'tag_fg': 'brown',
'threadline_authors_bg': 'default',
'threadline_authors_fg': 'dark green',
'threadline_authors_focus_bg': 'dark cyan',
'threadline_authors_focus_fg': 'black,bold',
'threadline_bg': 'default',
'threadline_content_bg': 'default',
'threadline_content_fg': 'dark gray',
'threadline_content_focus_bg': 'dark cyan',
'threadline_content_focus_fg': 'dark gray',
'threadline_date_bg': 'default',
'threadline_date_fg': 'light gray',
'threadline_date_focus_bg': 'dark cyan',
'threadline_date_focus_fg': 'black',
'threadline_fg': 'default',
'threadline_focus_bg': 'dark cyan',
'threadline_focus_fg': 'white',
'threadline_mailcount_bg': 'default',
'threadline_mailcount_fg': 'light gray',
'threadline_mailcount_focus_bg': 'dark cyan',
'threadline_mailcount_focus_fg': 'black',
'threadline_subject_bg': 'default',
'threadline_subject_fg': 'light gray',
'threadline_subject_focus_bg': 'dark cyan',
'threadline_subject_focus_fg': 'black',
'threadline_tags_bg': 'default',
'threadline_tags_fg': 'brown',
'threadline_tags_focus_bg': 'dark cyan',
'threadline_tags_focus_fg': 'yellow,bold',
},
'1c-theme': {
'bufferlist_focus': 'standout',
'bufferlist_results_even': 'default',
'bufferlist_results_odd': 'default',
'footer': 'standout',
'header': 'standout',
'message_attachment': 'default',
'message_attachment_focussed': 'underline',
'message_body': 'default',
'message_header': 'default',
'message_header_key': 'default',
'message_header_value': 'default',
'messagesummary_even': '',
'messagesummary_focus': 'standout',
'messagesummary_odd': '',
'notify_error': 'standout',
'notify_normal': 'default',
'prompt': '',
'tag_focus': 'standout',
'tag': 'default',
'threadline': 'default',
'threadline_authors': 'default,underline',
'threadline_authors_focus': 'standout',
'threadline_content': 'default',
'threadline_content_focus': 'standout',
'threadline_date': 'default',
'threadline_date_focus': 'standout',
'threadline_focus': 'standout',
'threadline_mailcount': 'default',
'threadline_mailcount_focus': 'standout',
'threadline_subject': 'default',
'threadline_subject_focus': 'standout',
'threadline_tags': 'bold',
'threadline_tags_focus': 'standout',
},
'256c-theme': {
'bufferlist_focus_bg': 'g38',
'bufferlist_focus_fg': '#ffa',
'bufferlist_results_even_bg': 'g3',
'bufferlist_results_even_fg': 'default',
'bufferlist_results_odd_bg': 'default',
'bufferlist_results_odd_fg': 'default',
'footer_bg': '#006',
'footer_fg': 'white',
'header_bg': 'dark blue',
'header_fg': 'white',
'message_attachment_bg': 'dark gray',
'message_attachment_fg': 'light gray',
'message_attachment_focussed_bg': 'light green',
'message_attachment_focussed_fg': 'light gray',
'message_body_bg': 'default',
'message_body_fg': 'light gray',
'message_header_bg': 'dark gray',
'message_header_fg': 'white',
'message_header_key_bg': 'dark gray',
'message_header_key_fg': 'white',
'message_header_value_bg': 'dark gray',
'message_header_value_fg': 'light gray',
'messagesummary_even_bg': '#068',
'messagesummary_even_fg': 'white',
'messagesummary_focus_bg': 'g58',
'messagesummary_focus_fg': '#ff8',
'messagesummary_odd_bg': '#006',
'messagesummary_odd_fg': 'white',
'notify_error_bg': 'dark red',
'notify_error_fg': 'white',
'notify_normal_bg': 'default',
'notify_normal_fg': 'default',
'prompt_bg': 'default',
'prompt_fg': 'light gray',
'tag_focus_bg': 'g58',
'tag_focus_fg': '#ffa',
'tag_bg': 'default',
'tag_fg': 'brown',
'threadline_authors_bg': 'default',
'threadline_authors_fg': '#6d6',
'threadline_authors_focus_bg': 'g58',
'threadline_authors_focus_fg': '#8f6',
'threadline_bg': 'default',
'threadline_content_bg': 'default',
'threadline_content_fg': '#866',
'threadline_content_focus_bg': 'g58',
'threadline_content_focus_fg': '#866',
'threadline_date_bg': 'default',
'threadline_date_fg': 'g58',
'threadline_date_focus_bg': 'g58',
'threadline_date_focus_fg': 'g89',
'threadline_fg': 'default',
'threadline_focus_bg': 'g58',
'threadline_focus_fg': 'white',
'threadline_mailcount_bg': 'default',
'threadline_mailcount_fg': 'light gray',
'threadline_mailcount_focus_bg': 'g58',
'threadline_mailcount_focus_fg': 'g89',
'threadline_subject_bg': 'default',
'threadline_subject_fg': 'g58',
'threadline_subject_focus_bg': 'g58',
'threadline_subject_focus_fg': 'g89',
'threadline_tags_bg': 'default',
'threadline_tags_fg': '#a86',
'threadline_tags_focus_bg': 'g58',
'threadline_tags_focus_fg': '#ff8',
},
'global-maps': {
'$': 'flush',
':': 'prompt',
';': 'bufferlist',
'@': 'refresh',
'@': 'refresh',
'I': 'search tag:inbox AND NOT tag:killed',
'L': 'taglist',
'U': 'search tag:unread',
'\\': 'prompt search ',
'm': 'compose',
'o': 'prompt search ',
'q': 'exit',
'shift tab': 'bprevious',
'tab': 'bnext',
'd': 'bclose',
},
'search-maps': {
'&': 'toggletag killed',
'O': 'refineprompt',
'a': 'toggletag inbox',
'enter': 'openthread',
'l': 'retagprompt',
'|': 'refineprompt',
},
'thread-maps': {
'C': 'fold --all',
'E': 'unfold --all',
'H': 'toggleheaders',
'P': 'print --all',
'a': 'toggletag inbox',
'enter': 'select',
'f': 'forward',
'g': 'groupreply',
'p': 'print',
'r': 'reply',
},
'taglist-maps': {
'enter': 'select',
},
'envelope-maps': {
'a': 'prompt attach ~/',
'y': 'send',
'enter': 'reedit',
't': 'prompt to ',
's': 'prompt subject ',
},
'bufferlist-maps': {
'x': 'closefocussed',
'enter': 'openfocussed',
},
'command-aliases': {
'clo': 'close',
'bn': 'bnext',
'bp': 'bprevious',
'ls': 'bufferlist',
'quit': 'exit',
}
}
NOTMUCH_DEFAULTS = {
'maildir': {
'synchronize_flags': 'False',
},
}
class DefaultsConfigParser(SafeConfigParser):
def __init__(self, defaults):
self.defaults = defaults
SafeConfigParser.__init__(self)
self.optionxform = lambda x: x
for sec in defaults.keys():
self.add_section(sec)
def get(self, section, option, fallback=None, *args, **kwargs):
if SafeConfigParser.has_option(self, section, option):
return SafeConfigParser.get(self, section, option, *args, **kwargs)
elif section in self.defaults:
if option in self.defaults[section]:
return self.defaults[section][option]
return fallback
def has_option(self, section, option, *args, **kwargs):
if SafeConfigParser.has_option(self, section, option):
return True
elif section in self.defaults:
if option in self.defaults[section]:
return True
return False
def getstringlist(self, section, option, **kwargs):
value = self.get(section, option, **kwargs)
return [s.strip() for s in value.split(',')]
class AlotConfigParser(DefaultsConfigParser):
def __init__(self, defaults):
DefaultsConfigParser.__init__(self, defaults)
self.hooks = None
def read(self, file):
if not os.path.isfile(file):
return
SafeConfigParser.readfp(self, codecs.open(file, "r", "utf8"))
if self.has_option('general', 'hooksfile'):
hf = os.path.expanduser(self.get('general', 'hooksfile'))
if hf is not None:
try:
config.hooks = imp.load_source('hooks', hf)
except:
pass
def get_palette(self):
mode = self.getint('general', 'colourmode')
ms = "%dc-theme" % mode
names = self.options(ms) + DEFAULTS[ms].keys()
if mode > 2:
names = set([s[:-3] for s in names])
p = list()
for attr in names:
nf = self.get('16c-theme', attr + '_fg', fallback='default')
nb = self.get('16c-theme', attr + '_bg', fallback='default')
m = self.get('1c-theme', attr, fallback='default')
hf = self.get('256c-theme', attr + '_fg', fallback='default')
hb = self.get('256c-theme', attr + '_bg', fallback='default')
p.append((attr, nf, nb, m, hf, hb))
if attr.startswith('tag_') and attr + '_focus' not in names:
nb = self.get('16c-theme', 'threadline_focus_bg',
fallback='default')
hb = self.get('256c-theme', 'threadline_focus_bg',
fallback='default')
p.append((attr + '_focus', nf, nb, m, hf, hb))
return p
def get_tagattr(self, tag, focus=False):
mode = self.getint('general', 'colourmode')
base = 'tag_%s' % tag
if mode == 2:
if self.get('1c-theme', base):
return 'tag_%s' % tag
elif mode == 16:
has_fg = self.get('16c-theme', base + '_fg')
has_bg = self.get('16c-theme', base + '_bg')
if has_fg or has_bg:
if focus:
return base + '_focus'
else:
return base
else: # highcolour
has_fg = self.get('256c-theme', base + '_fg')
has_bg = self.get('256c-theme', base + '_bg')
if has_fg or has_bg:
if focus:
return base + '_focus'
else:
return base
if focus:
return 'tag_focus'
return 'tag'
def get_mapping(self, mode, key):
cmdline = self.get(mode + '-maps', key)
if not cmdline:
cmdline = self.get('global-maps', key)
return cmdline
class HookManager:
def setup(self, hooksfile):
hf = os.path.expanduser(hooksfile)
if os.path.isfile(hf):
try:
self.module = imp.load_source('hooks', hf)
except:
self.module = None
else:
self.module = {}
def get(self, key):
if self.module:
if key in self.module.__dict__:
return self.module.__dict__[key]
def f(*args, **kwargs):
msg = 'called undefined hook: %s with arguments'
return f
config = AlotConfigParser(DEFAULTS)
notmuchconfig = DefaultsConfigParser(NOTMUCH_DEFAULTS)
hooks = HookManager()
mailcaps = mailcap.getcaps()
def get_mime_handler(mime_type, key='view', interactive=True):
if interactive:
mc_tuple = mailcap.findmatch(mailcaps,
mime_type,
key=key)
else:
mc_tuple = mailcap.findmatch(mailcaps,
mime_type,
key='copiousoutput')
if mc_tuple:
if mc_tuple[1]:
return mc_tuple[1][key]
else:
return None