summaryrefslogtreecommitdiff
path: root/alot
diff options
context:
space:
mode:
authorPatrick Totzke <patricktotzke@gmail.com>2012-01-04 09:38:23 -0800
committerPatrick Totzke <patricktotzke@gmail.com>2012-01-04 09:38:23 -0800
commit543ae78adea51c84b10125cfc375925bc93226ee (patch)
tree0ec8a2b8e7a7c4bb0cb89693d771386c1a668626 /alot
parente06acad3ecbfe9e0273e51881a58d21418c60601 (diff)
parentcdf3cf15cf12caa2c4dd854a42ca325df2db4491 (diff)
Merge pull request #226 from 0x64746b/query_highlighting
Query highlighting
Diffstat (limited to 'alot')
-rw-r--r--alot/db.py30
-rw-r--r--alot/defaults/alot.rc17
-rw-r--r--alot/message.py4
-rw-r--r--alot/settings.py64
-rw-r--r--alot/widgets.py22
5 files changed, 79 insertions, 58 deletions
diff --git a/alot/db.py b/alot/db.py
index 69256ea1..4a663ee3 100644
--- a/alot/db.py
+++ b/alot/db.py
@@ -362,22 +362,6 @@ class Thread(object):
"""returns id of this thread"""
return self._id
- def has_tags(self, *tags):
- """
- Checks whether this thread is tagged with the given tags.
-
- :param tags: tags to check
- :type tag: list[string]
- :returns: True if this thread is tagged with all the given tags, False
- otherwise.
- :rtype: bool
- """
- result = True
- for tag in tags:
- if not tag in self._tags:
- result = False
- return result
-
def get_tags(self):
"""
returns tagsstrings attached to this thread
@@ -512,3 +496,17 @@ class Thread(object):
def get_total_messages(self):
"""returns number of contained messages"""
return self._total_messages
+
+ def matches(self, query):
+ """
+ Check if this thread matches the given notmuch query.
+
+ :param query: The query to check against
+ :type query: string
+ :returns: True if this thread matches the given query, False otherwise
+ :rtype: bool
+ """
+ thread_query = 'thread:{tid} AND {subquery}'.format(tid=self._id,
+ subquery=query)
+ num_matches = self._dbman.count_messages(thread_query)
+ return num_matches > 0
diff --git a/alot/defaults/alot.rc b/alot/defaults/alot.rc
index 2f07ed9d..8e1e225f 100644
--- a/alot/defaults/alot.rc
+++ b/alot/defaults/alot.rc
@@ -35,17 +35,20 @@ terminal_cmd = x-terminal-emulator -e
######################
# HIGHLIGHT settings #
######################
-# Thread lines in the search buffer can be highlighted upon occurrence of a tag
+# Thread lines in the search buffer can be highlighted if they match a query
# by theming their components.
-# comma separated list of tag combinations you want highlighting for. Note that
-# the sequence of the list defines the search order. The first specified tag
-# combination that is found defines the themeing. Tag combinations are joined by
-# the '+' character.
-thread_highlight_tags = unread+flagged,unread,flagged
+# dictionary of highlighting rules. The keys are queries you want highlighting
+# for; values are chosen designators that identify themeing options in the
+# colour scheme:
+# search_thread_<component>_[focus_]<id>_[fg|bg]
+# Note that the sequence of the list defines the search order. The first
+# specified query that matches selects the themeing.
+thread_highlight_rules = { "tag:unread AND tag:flagged":"unread+flagged",
+ "tag:unread":"unread", "tag:flagged":"flagged" }
# comma separated list of the components of a thread line you want highlighted
-# if a configured tag is found.
+# if a query matches.
# Possible components are [date|mailcount|authors|subject].
thread_highlight_components = subject
diff --git a/alot/message.py b/alot/message.py
index 1be06bcb..36f2a5ff 100644
--- a/alot/message.py
+++ b/alot/message.py
@@ -134,8 +134,8 @@ class Message(object):
"""
if self._datetime == None:
return None
- formatstring = config.get('general', 'timestamp_format')
- if formatstring:
+ if config.has_option('general', 'timestamp_format'):
+ formatstring = config.get('general', 'timestamp_format')
res = self._datetime.strftime(formatstring)
else:
res = helper.pretty_datetime(self._datetime)
diff --git a/alot/settings.py b/alot/settings.py
index 72d5a7a1..a9cdb279 100644
--- a/alot/settings.py
+++ b/alot/settings.py
@@ -2,11 +2,13 @@ import imp
import os
import re
import ast
+import json
import mailcap
import codecs
import logging
-from ConfigParser import SafeConfigParser
+from collections import OrderedDict
+from ConfigParser import SafeConfigParser, ParsingError, NoOptionError
class FallbackConfigParser(SafeConfigParser):
@@ -25,15 +27,20 @@ class FallbackConfigParser(SafeConfigParser):
:param fallback: the value to fall back if option undefined
:type fallback: str
"""
-
if SafeConfigParser.has_option(self, section, option):
return SafeConfigParser.get(self, section, option, *args, **kwargs)
- return fallback
+ elif fallback != None:
+ return fallback
+ else:
+ raise NoOptionError(option, section)
def getstringlist(self, section, option, **kwargs):
"""directly parses a config value into a list of strings"""
- value = self.get(section, option, **kwargs)
- return [s.strip() for s in value.split(',') if s.strip()]
+ stringlist = list()
+ if self.has_option(section, option):
+ value = self.get(section, option, **kwargs)
+ stringlist = [s.strip() for s in value.split(',') if s.strip()]
+ return stringlist
class AlotConfigParser(FallbackConfigParser):
@@ -163,17 +170,26 @@ class AlotConfigParser(FallbackConfigParser):
has_bg = self.has_option(theme, themeing + '_bg')
return (has_fg or has_bg)
- def get_highlight_tags(self):
- if self.has_option('general', 'thread_highlight_tags'):
- highlight_tags = list()
- raw_lists = self.getstringlist('general', 'thread_highlight_tags')
- for raw_list in raw_lists:
- raw_combo = raw_list.split('+')
- tag_combo = [tag.strip() for tag in raw_combo]
- highlight_tags.append(tag_combo)
- return highlight_tags
- else:
- raise NameError("No config option 'thread_highlight_tags'.")
+ def get_highlight_rules(self):
+ """
+ Parse the highlighting rules from the config file.
+
+ :returns: The highlighting rules
+ :rtype: :py:class:`collections.OrderedDict`
+ """
+ rules = OrderedDict()
+ try:
+ config_string = self.get('general', 'thread_highlight_rules')
+ rules = json.loads(config_string, object_pairs_hook=OrderedDict)
+ except NoOptionError as err:
+ logging.exception(err)
+ except ValueError as err:
+ report = ParsingError("Could not parse config option" \
+ " 'thread_highlight_rules' in section" \
+ " 'general': {reason}".format(reason=err))
+ logging.exception(report)
+ finally:
+ return rules
def get_tagattr(self, tag, focus=False):
"""
@@ -188,19 +204,19 @@ class AlotConfigParser(FallbackConfigParser):
mode = self.getint('general', 'colourmode')
base = 'tag_%s' % tag
if mode == 2:
- if self.get('1c-theme', base):
+ if self.has_option('1c-theme', base):
return base
elif mode == 16:
- has_fg = self.get('16c-theme', base + '_fg')
- has_bg = self.get('16c-theme', base + '_bg')
+ has_fg = self.has_option('16c-theme', base + '_fg')
+ has_bg = self.has_option('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')
+ has_fg = self.has_option('256c-theme', base + '_fg')
+ has_bg = self.has_option('256c-theme', base + '_bg')
if has_fg or has_bg:
if focus:
return base + '_focus'
@@ -239,8 +255,10 @@ class AlotConfigParser(FallbackConfigParser):
:returns: a command line to be applied upon keypress
:rtype: str
"""
- cmdline = self.get(mode + '-maps', key)
- if not cmdline:
+ cmdline = None
+ if self.has_option(mode + '-maps', key):
+ cmdline = self.get(mode + '-maps', key)
+ elif self.has_option('global-maps', key):
cmdline = self.get('global-maps', key)
return cmdline
diff --git a/alot/widgets.py b/alot/widgets.py
index 9a024afa..17141917 100644
--- a/alot/widgets.py
+++ b/alot/widgets.py
@@ -79,7 +79,7 @@ class ThreadlineWidget(urwid.AttrMap):
'display_content_in_threadline')
self.highlight_components = config.getstringlist('general',
'thread_highlight_components')
- self.highlight_tags = config.get_highlight_tags()
+ self.highlight_rules = config.get_highlight_rules()
self.rebuild()
urwid.AttrMap.__init__(self, self.columns,
'search_thread', 'search_thread_focus')
@@ -93,8 +93,8 @@ class ThreadlineWidget(urwid.AttrMap):
if newest == None:
datestring = u' ' * 10
else:
- formatstring = config.get('general', 'timestamp_format')
- if formatstring:
+ if config.has_option('general', 'timestamp_format'):
+ formatstring = config.get('general', 'timestamp_format')
datestring = newest.strftime(formatstring)
else:
datestring = pretty_datetime(newest).rjust(10)
@@ -192,9 +192,9 @@ class ThreadlineWidget(urwid.AttrMap):
def _get_highlight_theme_suffix(self):
suffix = None
- for tags in self.highlight_tags:
- if self.thread.has_tags(*tags):
- suffix = '+'.join(tags)
+ for query in self.highlight_rules.keys():
+ if self.thread.matches(query):
+ suffix = self.highlight_rules[query]
break
return suffix
@@ -202,10 +202,12 @@ class ThreadlineWidget(urwid.AttrMap):
theme = 'search_thread_{0}'.format(component)
if focus:
theme = theme + '_focus'
- if self.highlight_theme_suffix and component in self.highlight_components:
- tag_theme = theme + '_{tags}'.format(tags=self.highlight_theme_suffix)
- if config.has_themeing(tag_theme):
- theme = tag_theme
+ if (self.highlight_theme_suffix and
+ component in self.highlight_components):
+ highlight_theme = (theme +
+ '_{id}'.format(id=self.highlight_theme_suffix))
+ if config.has_themeing(highlight_theme):
+ theme = highlight_theme
return theme