diff options
author | pazz <patricktotzke@gmail.com> | 2011-06-04 16:59:42 +0100 |
---|---|---|
committer | pazz <patricktotzke@gmail.com> | 2011-06-04 16:59:42 +0100 |
commit | 66a4dacb2a0ab7d852556376916cab85ef1fd528 (patch) | |
tree | 2943fb290b3534b446f345af7079003d1c4581f4 | |
parent | a3bfc92edbcb939eb95de49fe1cfc396c1422331 (diff) | |
parent | d8d8451bb2d45e058e43ccd402a538ed6abee06f (diff) |
Merge branch 'shiny'
-rw-r--r-- | alot/buffer.py | 59 | ||||
-rw-r--r-- | alot/db.py | 2 | ||||
-rw-r--r-- | alot/helper.py | 2 | ||||
-rw-r--r-- | alot/widgets.py | 129 |
4 files changed, 144 insertions, 48 deletions
diff --git a/alot/buffer.py b/alot/buffer.py index 6b3fc011..68069318 100644 --- a/alot/buffer.py +++ b/alot/buffer.py @@ -21,6 +21,7 @@ import urwid import widgets import command from walker import IteratorWalker +from itertools import izip_longest class Buffer: @@ -171,9 +172,6 @@ class SingleThreadBuffer(Buffer): def __init__(self, ui, thread): self.message_count = thread.get_total_messages() self.thread = thread - self.messages = list() - for (m,r) in thread.get_messages().items(): - self._build_pile(self.messages, m, r) self.rebuild() Buffer.__init__(self, ui, self.body, 'thread') self.bindings = {} @@ -181,28 +179,43 @@ class SingleThreadBuffer(Buffer): def __str__(self): return '%s, (%d)' % (self.thread.subject, self.message_count) - def _build_pile(self, acc, msg, replies, depth=0): - acc.append((depth, msg)) - for (m,r) in replies.items(): - self._build_pile(acc, m, r, depth+1) + def _build_pile(self, acc, childcount, msg, replies, parent, depth=0): + acc.append((parent, depth, msg)) + childcount[parent] += 1 + for (reply, rereplies) in replies.items(): + if reply not in childcount: + childcount[reply] = 0 + self._build_pile(acc, childcount, reply, rereplies, msg, depth + 1) def rebuild(self): - msgs = list() - for (num, (depth, m)) in enumerate(self.messages, 1): - mwidget = widgets.MessageWidget(m, folded=True) - if (num % 2 == 0): - attr = 'messagesummary_even' - else: - attr = 'messagesummary_odd' - m.remove_tags(['unread']) - # a spacer of width 0 breaks urwid.Columns - if depth == 0: - line = urwid.AttrMap(urwid.Columns([mwidget]), attr, 'messagesummary_focus') - else: - spacer = urwid.Text(' ' * depth) - line = urwid.AttrMap(urwid.Columns([('fixed', depth, spacer), mwidget]), attr, 'messagesummary_focus') - msgs.append(line) - self.body = urwid.ListBox(msgs) + # depth-first traversing the thread-tree, thereby + # 1) build a list of tuples (parentmsg, depth, message) in DF order + # 2) create a dict that counts no. of direct replies per message + messages = list() # accumulator for 1, + childcount = {None: 0} # accumulator for 2) + # start with all toplevel msgs, then recursively call _build_pile + for (msg, replies) in self.thread.get_messages().items(): + if msg not in childcount: # in create entry for current msg + childcount[msg] = 0 + self._build_pile(messages, childcount, msg, replies, None) + + # go through list from 1) and pile up message widgets for all msgs. + # each one will be given its depth, if siblings follow and where to + # draw bars (siblings follow at lower depths) + msglines = list() + bars = [] + for (num, (p, depth, m)) in enumerate(messages): + bars = bars[:depth] + childcount[p] -= 1 + + bars.append(childcount[p] > 0) + mwidget = widgets.MessageWidget(m, even=(num % 2 == 0), + unfold_header=False, # settings + unfold_body=False, + depth=depth, + bars_at=bars) + msglines.append(mwidget) + self.body = urwid.ListBox(msglines) def get_selected_message(self): (messagewidget, size) = self.body.get_focus() @@ -178,6 +178,8 @@ class Message: def __init__(self, dbman, msg): self.dbman = dbman self.mid = msg.get_message_id() + self.datetime = datetime.fromtimestamp(msg.get_date()) + self.sender = msg.get_header('From') self.strrep = str(msg) self.email = None # will be read upon first use self.filename = msg.get_filename() diff --git a/alot/helper.py b/alot/helper.py index e4a2f0c8..06fa986e 100644 --- a/alot/helper.py +++ b/alot/helper.py @@ -37,4 +37,4 @@ def pretty_datetime(d): string = d.strftime('%b %Y') else: string = d.strftime('%b %d') - return string.rjust(10) + return string diff --git a/alot/widgets.py b/alot/widgets.py index 2a7eee69..65af5433 100644 --- a/alot/widgets.py +++ b/alot/widgets.py @@ -17,6 +17,7 @@ along with notmuch. If not, see <http://www.gnu.org/licenses/>. Copyright (C) 2011 Patrick Totzke <patricktotzke@gmail.com> """ import email +import urwid from urwid import Text from urwid import Edit from urwid import Pile @@ -26,6 +27,7 @@ from urwid import WidgetWrap from urwid import ListBox from urwid import SimpleListWalker from datetime import datetime +import logging import settings from helper import shorten @@ -41,7 +43,7 @@ class ThreadlineWidget(AttrMap): def rebuild(self): cols = [] - datestring = pretty_datetime(self.thread.get_newest_date()) + datestring = pretty_datetime(self.thread.get_newest_date()).rjust(10) self.date_w = AttrMap(Text(datestring), 'threadline_date') cols.append(('fixed', len(datestring), self.date_w)) @@ -150,8 +152,9 @@ class PromptWidget(AttrMap): if self.completer: pos = self.start_completion_pos original = self.editpart.edit_text[:pos] - if not self.completion_results: #not already in completion mode - self.completion_results = [''] + self.completer.complete(original) + if not self.completion_results: # not in completion mode + self.completion_results = [''] + \ + self.completer.complete(original) self.focus_in_clist = 1 else: if key == 'tab': @@ -176,33 +179,108 @@ class PromptWidget(AttrMap): class MessageWidget(WidgetWrap): - def __init__(self, message, folded=True): + def __init__(self, message, even=False, unfold_body=False, + unfold_header=False, depth=0, bars_at=[]): self.message = message - self.sumw = MessageSummaryWidget(self.message) - self.headerw = MessageHeaderWidget(self.message.get_email()) - self.bodyw = MessageBodyWidget(self.message.get_email()) - self.displayed_list = [self.sumw] - if not folded: - self.displayed_list.append(self.bodyw) - self.body = Pile(self.displayed_list) - WidgetWrap.__init__(self, self.body) + self.depth = depth + self.bars_at = bars_at + self.even = even + + # build the summary line, header and body will be created on demand + self.sumline = self._build_sum_line() + self.headerw = None + self.bodyw = None + self.displayed_list = [self.sumline] + if unfold_header: + self.displayed_list.append(self.get_header_widget()) + if unfold_body: + self.displayed_list.append(self.get_body_widget()) + + #build pile and call super constructor + self.pile = Pile(self.displayed_list) + WidgetWrap.__init__(self, self.pile) + + # in case the message is yet unread, remove this tag + if 'unread' in message.get_tags(): + message.remove_tags(['unread']) def rebuild(self): - self.body = Pile(self.displayed_list) - self._w = self.body + self.pile = Pile(self.displayed_list) + self._w = self.pile + + def _build_sum_line(self): + """creates/returns the widget that displays the summary line.""" + self.sumw = MessageSummaryWidget(self.message) + if self.even: + attr = 'messagesummary_even' + else: + attr = 'messagesummary_odd' + cols = [] + bc = list() # box_columns + if self.depth > 1: + bc.append(0) + cols.append(self._get_spacer(self.bars_at[1:-1])) + if self.depth > 0: + if self.bars_at[-1]: + arrowhead = u'\u251c\u25b6' + else: + arrowhead = u'\u2514\u25b6' + cols.append(('fixed', 2, Text(arrowhead))) + cols.append(self.sumw) + line = urwid.AttrMap(urwid.Columns(cols, box_columns=bc), + attr, 'messagesummary_focus') + return line + + def _get_header_widget(self): + """creates/returns the widget that displays the mail header""" + if not self.headerw: + cols = [MessageHeaderWidget(self.message.get_email())] + bc = list() + if self.depth: + cols.insert(0, self._get_spacer(self.bars_at[1:])) + bc.append(0) + self.headerw = urwid.Columns(cols, box_columns=bc) + return self.headerw + + def _get_body_widget(self): + """creates/returns the widget that displays the mail body""" + if not self.bodyw: + cols = [MessageBodyWidget(self.message.get_email())] + bc = list() + if self.depth: + cols.insert(0, self._get_spacer(self.bars_at[1:])) + bc.append(0) + self.bodyw = urwid.Columns(cols, box_columns=bc) + return self.bodyw + + def _get_spacer(self, bars_at): + prefixchars = [] + logging.info(bars_at) + length = len(bars_at) + for b in bars_at: + if b: + c = u'\u2502' + else: + c = ' ' + prefixchars.append(('fixed', 1, urwid.SolidFill(c))) + + spacer = urwid.Columns(prefixchars, box_columns=range(length)) + return ('fixed', length, spacer) def toggle_header(self): - if self.headerw in self.displayed_list: - self.displayed_list.remove(self.headerw) + hw = self._get_header_widget() + if hw in self.displayed_list: + self.displayed_list.remove(hw) else: - self.displayed_list.insert(1, self.headerw) + self.displayed_list.insert(1, hw) self.rebuild() def toggle_body(self): - if self.bodyw in self.displayed_list: - self.displayed_list.remove(self.bodyw) + bw = self._get_body_widget() + if bw in self.displayed_list: + self.displayed_list.remove(bw) else: - self.displayed_list.append(self.bodyw) + self.displayed_list.append(bw) self.sumw.toggle_folded() self.rebuild() @@ -215,7 +293,7 @@ class MessageWidget(WidgetWrap): elif key == 'enter': self.toggle_body() else: - return self.body.keypress(size, key) + return self.pile.keypress(size, key) def get_message(self): return self.message @@ -225,16 +303,19 @@ class MessageWidget(WidgetWrap): class MessageSummaryWidget(WidgetWrap): + """a one line summary of a message, top of the message widget pile.""" + def __init__(self, message, folded=True): self.message = message self.folded = folded WidgetWrap.__init__(self, Text(str(self))) def __str__(self): - prefix = "-" + prefix = "- " if self.folded: - prefix = '+' - return prefix + str(self.message) + prefix = '+ ' + return "%s%s (%s)" % (prefix, self.message.sender, + pretty_datetime(self.message.datetime)) def toggle_folded(self): self.folded = not self.folded |