From 7efd5f34f344c519bc46309dc55eb85491a1ce00 Mon Sep 17 00:00:00 2001 From: pazz Date: Wed, 1 Jun 2011 18:33:50 +0100 Subject: lazy messages, rebuild of singlethread msg lines --- alot/widgets.py | 43 +++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) (limited to 'alot/widgets.py') diff --git a/alot/widgets.py b/alot/widgets.py index 2a7eee69..5e5bace3 100644 --- a/alot/widgets.py +++ b/alot/widgets.py @@ -176,14 +176,15 @@ class PromptWidget(AttrMap): class MessageWidget(WidgetWrap): - def __init__(self, message, folded=True): + def __init__(self, message, depth=0, folded=True): self.message = message + self.depth = depth self.sumw = MessageSummaryWidget(self.message) - self.headerw = MessageHeaderWidget(self.message.get_email()) - self.bodyw = MessageBodyWidget(self.message.get_email()) + self.headerw = None + self.bodyw = None self.displayed_list = [self.sumw] if not folded: - self.displayed_list.append(self.bodyw) + self.displayed_list.append(self.get_body_widget()) self.body = Pile(self.displayed_list) WidgetWrap.__init__(self, self.body) @@ -191,18 +192,36 @@ class MessageWidget(WidgetWrap): self.body = Pile(self.displayed_list) self._w = self.body + def _get_spacer(self): + if self.depth: + return urwid.Text((' ' * self.depth) + u'\u2514\u25b6') + else: + return None + + def get_header_widget(self): + if not self.headerw: + self.headerw = MessageHeaderWidget(self.message.get_email()) + return self.headerw + + def get_body_widget(self): + if not self.bodyw: + self.bodyw = MessageBodyWidget(self.message.get_email()) + return self.bodyw + 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() @@ -231,9 +250,9 @@ class MessageSummaryWidget(WidgetWrap): WidgetWrap.__init__(self, Text(str(self))) def __str__(self): - prefix = "-" + prefix = "- " if self.folded: - prefix = '+' + prefix = '+ ' return prefix + str(self.message) def toggle_folded(self): -- cgit v1.2.3 From 84c43327beba5718aeb80dbdc80fc247ab8c4262 Mon Sep 17 00:00:00 2001 From: pazz Date: Fri, 3 Jun 2011 09:45:05 +0100 Subject: arrows in single thread buffer --- alot/buffer.py | 48 +++++++++++++++++++++++++-------------------- alot/widgets.py | 60 +++++++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 79 insertions(+), 29 deletions(-) (limited to 'alot/widgets.py') diff --git a/alot/buffer.py b/alot/buffer.py index 3819d23d..d0770b96 100644 --- a/alot/buffer.py +++ b/alot/buffer.py @@ -21,6 +21,8 @@ import urwid import widgets import command from walker import IteratorWalker +import logging +from itertools import izip_longest class Buffer: @@ -171,9 +173,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,29 +180,36 @@ 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)) + def _build_pile(self, acc, childcount, msg, replies, parent, depth=0): + acc.append((parent, depth, msg)) + childcount[parent] += 1 for (m,r) in replies.items(): - self._build_pile(acc, m, r, depth+1) + if m not in childcount: + childcount[m] = 0 + self._build_pile(acc, childcount, m, r, msg, depth+1) def rebuild(self): - msgs = list() - for (num, (depth, m)) in enumerate(self.messages, 1): + messages = list() + childcount = {None: 0} + for (m,r) in self.thread.get_messages().items(): + childcount[None] += 1 + if m not in childcount: + childcount[m] = 0 + self._build_pile(messages, childcount, m, r, None) + logging.info('>>> %s'%(childcount)) + msglines = list() + for (num, (p, depth, m)) in enumerate(messages): if 'unread' in m.get_tags(): m.remove_tags(['unread']) - mwidget = widgets.MessageWidget(m, depth=depth, folded=True) - if (num % 2 == 0): - attr = 'messagesummary_even' - else: - attr = 'messagesummary_odd' - # 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) + u'\u2514\u25b6') - line = urwid.AttrMap(urwid.Columns([('fixed', depth+3, spacer), mwidget]), attr, 'messagesummary_focus') - msgs.append(line) - self.body = urwid.ListBox(msgs) + childcount[p] -=1 + mwidget = widgets.MessageWidget(m, even=(num % 2 == 0), + folded=True, depth=depth, + bars_at=[], + siblings_follow=childcount[p] + ) + msglines.append(mwidget) + logging.info('>>> %s'%(childcount)) + self.body = urwid.ListBox(msglines) def get_selected_message(self): (messagewidget, size) = self.body.get_focus() diff --git a/alot/widgets.py b/alot/widgets.py index 5e5bace3..8771c0b3 100644 --- a/alot/widgets.py +++ b/alot/widgets.py @@ -17,6 +17,7 @@ along with notmuch. If not, see . Copyright (C) 2011 Patrick Totzke """ 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 @@ -176,10 +178,14 @@ class PromptWidget(AttrMap): class MessageWidget(WidgetWrap): - def __init__(self, message, depth=0, folded=True): + def __init__(self, message, even=False, folded=True, depth=0, bars_at=[], + siblings_follow=False): self.message = message self.depth = depth - self.sumw = MessageSummaryWidget(self.message) + self.bars_at = bars_at + self.siblings_follow = siblings_follow + self.even = even + self.sumw = self._build_sum_widget() self.headerw = None self.bodyw = None self.displayed_list = [self.sumw] @@ -188,24 +194,62 @@ class MessageWidget(WidgetWrap): self.body = Pile(self.displayed_list) WidgetWrap.__init__(self, self.body) + def _build_sum_widget(self): + sumw = MessageSummaryWidget(self.message) + if self.even: + attr = 'messagesummary_even' + else: + attr = 'messagesummary_odd' + # a spacer of width 0 breaks urwid.Columns + cols = [] + bc = list() # box_columns + if self.depth > 0: + bc.append(0) + cols.append(self._get_spacer()) + if self.siblings_follow: + arrowhead = u'\u251c\u25b6' + else: + arrowhead = u'\u2514\u25b6' + cols.append(('fixed', 2, Text(arrowhead))) + cols.append(sumw) + line = urwid.AttrMap(urwid.Columns(cols, box_columns=bc), attr, 'messagesummary_focus') + return line + + def rebuild(self): self.body = Pile(self.displayed_list) self._w = self.body def _get_spacer(self): - if self.depth: - return urwid.Text((' ' * self.depth) + u'\u2514\u25b6') - else: - return None + prefixchars = [] + for i in range(self.depth): + if i in self.bars_at: + c = u'\u2502' + else: + c = ' ' + prefixchars.append(('fixed', 1, urwid.SolidFill(c))) + + spacer = urwid.Columns(prefixchars, box_columns=range(self.depth)) + return ('fixed', self.depth, spacer) def get_header_widget(self): if not self.headerw: - self.headerw = MessageHeaderWidget(self.message.get_email()) + cols = [MessageHeaderWidget(self.message.get_email())] + bc = list() + if self.depth: + cols.insert(0,self._get_spacer()) + bc.append(0) + self.headerw = urwid.Columns(cols, box_columns = bc) return self.headerw def get_body_widget(self): if not self.bodyw: - self.bodyw = MessageBodyWidget(self.message.get_email()) + cols = [MessageBodyWidget(self.message.get_email())] + bc = list() + if self.depth: + cols.insert(0,self._get_spacer()) + bc.append(0) + self.bodyw = urwid.Columns(cols, box_columns = bc) return self.bodyw def toggle_header(self): -- cgit v1.2.3 From 34f707147e238be6191ec32d587084bd0bcb8c99 Mon Sep 17 00:00:00 2001 From: pazz Date: Sat, 4 Jun 2011 13:46:04 +0100 Subject: shiny arrows for single thread view --- alot/buffer.py | 40 ++++++++++++++++++++++++++-------------- alot/widgets.py | 57 +++++++++++++++++++++++++++++++-------------------------- 2 files changed, 57 insertions(+), 40 deletions(-) (limited to 'alot/widgets.py') diff --git a/alot/buffer.py b/alot/buffer.py index d0770b96..f29f124f 100644 --- a/alot/buffer.py +++ b/alot/buffer.py @@ -183,29 +183,41 @@ class SingleThreadBuffer(Buffer): def _build_pile(self, acc, childcount, msg, replies, parent, depth=0): acc.append((parent, depth, msg)) childcount[parent] += 1 - for (m,r) in replies.items(): - if m not in childcount: - childcount[m] = 0 - self._build_pile(acc, childcount, m, r, msg, depth+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): - messages = list() - childcount = {None: 0} - for (m,r) in self.thread.get_messages().items(): - childcount[None] += 1 - if m not in childcount: - childcount[m] = 0 - self._build_pile(messages, childcount, m, r, None) + # 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) + for (msg,replies) in self.thread.get_messages().items(): # all toplevel msgs + if msg not in childcount: # in create entry for current msg + childcount[msg] = 0 + self._build_pile(messages, childcount, msg, replies, None) # recursion + logging.info('>>> %s'%(childcount)) + # 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): + # in case the message is yet unread, remove this tag if 'unread' in m.get_tags(): m.remove_tags(['unread']) + bars = bars[:depth] childcount[p] -=1 + + bars.append(childcount[p]>0) mwidget = widgets.MessageWidget(m, even=(num % 2 == 0), - folded=True, depth=depth, - bars_at=[], - siblings_follow=childcount[p] + unfold_header=False, # settings + unfold_body=False, + depth=depth, + bars_at=bars, ) msglines.append(mwidget) logging.info('>>> %s'%(childcount)) diff --git a/alot/widgets.py b/alot/widgets.py index 8771c0b3..407c4575 100644 --- a/alot/widgets.py +++ b/alot/widgets.py @@ -178,66 +178,71 @@ class PromptWidget(AttrMap): class MessageWidget(WidgetWrap): - def __init__(self, message, even=False, folded=True, depth=0, bars_at=[], - siblings_follow=False): + def __init__(self, message, even=False, unfold_body=False, + unfold_header=False, depth=0, bars_at=[]): self.message = message self.depth = depth self.bars_at = bars_at - self.siblings_follow = siblings_follow self.even = even - self.sumw = self._build_sum_widget() + + # 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.sumw] - if not folded: + 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()) - self.body = Pile(self.displayed_list) - WidgetWrap.__init__(self, self.body) + self.pile = Pile(self.displayed_list) + WidgetWrap.__init__(self, self.pile) - def _build_sum_widget(self): - sumw = MessageSummaryWidget(self.message) + def _build_sum_line(self): + self.sumw = MessageSummaryWidget(self.message) if self.even: attr = 'messagesummary_even' else: attr = 'messagesummary_odd' - # a spacer of width 0 breaks urwid.Columns cols = [] bc = list() # box_columns - if self.depth > 0: + if self.depth > 1: bc.append(0) - cols.append(self._get_spacer()) - if self.siblings_follow: + 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(sumw) + cols.append(self.sumw) line = urwid.AttrMap(urwid.Columns(cols, box_columns=bc), attr, 'messagesummary_focus') return line def rebuild(self): - self.body = Pile(self.displayed_list) - self._w = self.body + self.pile = Pile(self.displayed_list) + self._w = self.pile - def _get_spacer(self): + def _get_spacer(self, bars_at): prefixchars = [] - for i in range(self.depth): - if i in self.bars_at: + 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(self.depth)) - return ('fixed', self.depth, spacer) + spacer = urwid.Columns(prefixchars, box_columns=range(length)) + return ('fixed', length, spacer) def get_header_widget(self): if not self.headerw: cols = [MessageHeaderWidget(self.message.get_email())] bc = list() if self.depth: - cols.insert(0,self._get_spacer()) + cols.insert(0,self._get_spacer(self.bars_at[1:])) bc.append(0) self.headerw = urwid.Columns(cols, box_columns = bc) return self.headerw @@ -247,9 +252,9 @@ class MessageWidget(WidgetWrap): cols = [MessageBodyWidget(self.message.get_email())] bc = list() if self.depth: - cols.insert(0,self._get_spacer()) + cols.insert(0,self._get_spacer(self.bars_at[1:])) bc.append(0) - self.bodyw = urwid.Columns(cols, box_columns = bc) + self.bodyw = urwid.Columns(cols, box_columns=bc) return self.bodyw def toggle_header(self): @@ -278,7 +283,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 -- cgit v1.2.3 From 16d08eac67f6766fa87b99b642c784f06bcf9682 Mon Sep 17 00:00:00 2001 From: pazz Date: Sat, 4 Jun 2011 16:30:23 +0100 Subject: cleanup, pep8, comments --- alot/buffer.py | 28 ++++++++++-------------- alot/widgets.py | 68 +++++++++++++++++++++++++++++++++------------------------ 2 files changed, 51 insertions(+), 45 deletions(-) (limited to 'alot/widgets.py') diff --git a/alot/buffer.py b/alot/buffer.py index f29f124f..68069318 100644 --- a/alot/buffer.py +++ b/alot/buffer.py @@ -21,7 +21,6 @@ import urwid import widgets import command from walker import IteratorWalker -import logging from itertools import izip_longest @@ -186,41 +185,36 @@ class SingleThreadBuffer(Buffer): for (reply, rereplies) in replies.items(): if reply not in childcount: childcount[reply] = 0 - self._build_pile(acc, childcount, reply, rereplies, msg, depth+1) + self._build_pile(acc, childcount, reply, rereplies, msg, depth + 1) def rebuild(self): # 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) - for (msg,replies) in self.thread.get_messages().items(): # all toplevel msgs + 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) # recursion + self._build_pile(messages, childcount, msg, replies, None) - logging.info('>>> %s'%(childcount)) # 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) + # draw bars (siblings follow at lower depths) msglines = list() bars = [] for (num, (p, depth, m)) in enumerate(messages): - # in case the message is yet unread, remove this tag - if 'unread' in m.get_tags(): - m.remove_tags(['unread']) bars = bars[:depth] - childcount[p] -=1 + childcount[p] -= 1 - bars.append(childcount[p]>0) + bars.append(childcount[p] > 0) mwidget = widgets.MessageWidget(m, even=(num % 2 == 0), - unfold_header=False, # settings + unfold_header=False, # settings unfold_body=False, depth=depth, - bars_at=bars, - ) + bars_at=bars) msglines.append(mwidget) - logging.info('>>> %s'%(childcount)) self.body = urwid.ListBox(msglines) def get_selected_message(self): diff --git a/alot/widgets.py b/alot/widgets.py index 407c4575..97a90b40 100644 --- a/alot/widgets.py +++ b/alot/widgets.py @@ -152,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': @@ -194,10 +195,21 @@ class MessageWidget(WidgetWrap): 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.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' @@ -215,13 +227,31 @@ class MessageWidget(WidgetWrap): 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') + 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 rebuild(self): - self.pile = Pile(self.displayed_list) - self._w = self.pile + 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 = [] @@ -237,28 +267,8 @@ class MessageWidget(WidgetWrap): spacer = urwid.Columns(prefixchars, box_columns=range(length)) return ('fixed', length, spacer) - def get_header_widget(self): - 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): - 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 toggle_header(self): - hw = self.get_header_widget() + hw = self._get_header_widget() if hw in self.displayed_list: self.displayed_list.remove(hw) else: @@ -266,7 +276,7 @@ class MessageWidget(WidgetWrap): self.rebuild() def toggle_body(self): - bw = self.get_body_widget() + bw = self._get_body_widget() if bw in self.displayed_list: self.displayed_list.remove(bw) else: @@ -293,6 +303,8 @@ 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 -- cgit v1.2.3 From d8d8451bb2d45e058e43ccd402a538ed6abee06f Mon Sep 17 00:00:00 2001 From: pazz Date: Sat, 4 Jun 2011 16:55:41 +0100 Subject: some love for the message summary widget --- alot/db.py | 2 ++ alot/helper.py | 2 +- alot/widgets.py | 5 +++-- 3 files changed, 6 insertions(+), 3 deletions(-) (limited to 'alot/widgets.py') diff --git a/alot/db.py b/alot/db.py index a063d9a1..4868d7a7 100644 --- a/alot/db.py +++ b/alot/db.py @@ -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 97a90b40..65af5433 100644 --- a/alot/widgets.py +++ b/alot/widgets.py @@ -43,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)) @@ -314,7 +314,8 @@ class MessageSummaryWidget(WidgetWrap): prefix = "- " if self.folded: prefix = '+ ' - return prefix + str(self.message) + return "%s%s (%s)" % (prefix, self.message.sender, + pretty_datetime(self.message.datetime)) def toggle_folded(self): self.folded = not self.folded -- cgit v1.2.3