def rebuild(self): try: self.thread.refresh() except NonexistantObjectError: self.body = urwid.SolidFill() self.message_count = 0 return self._tree = ThreadTree(self.thread) # define A to be the tree to be wrapped by a NestedTree and displayed. # We wrap the thread tree into an ArrowTree for decoration if # indentation was requested and otherwise use it as is. if self._indent_width == 0: A = self._tree else: # we want decoration. bars_att = settings.get_theming_attribute('thread', 'arrow_bars') # only add arrow heads if there is space (indent > 1). heads_char = None heads_att = None if self._indent_width > 1: heads_char = '➤' heads_att = settings.get_theming_attribute( 'thread', 'arrow_heads') A = ArrowTree(self._tree, indent=self._indent_width, childbar_offset=0, arrow_tip_att=heads_att, arrow_tip_char=heads_char, arrow_att=bars_att) self._nested_tree = NestedTree(A, interpret_covered=True) self.body = TreeBox(self._nested_tree) self.message_count = self.thread.get_total_messages()
def rebuild(self): try: self.thread.refresh() except NonexistantObjectError: self.body = urwid.SolidFill() self.message_count = 0 return self._tree = ThreadTree(self.thread) bars_att = settings.get_theming_attribute('thread', 'arrow_bars') heads_att = settings.get_theming_attribute('thread', 'arrow_heads') A = ArrowTree(self._tree, indent=2, childbar_offset=0, arrow_tip_att=heads_att, arrow_att=bars_att) self._nested_tree = NestedTree(A, interpret_covered=True) self.body = TreeBox(self._nested_tree) self.message_count = self.thread.get_total_messages()
def rebuild(self): try: self.thread.refresh() except NonexistantObjectError: self.body = urwid.SolidFill() self.message_count = 0 return self._tree = ThreadTree(self.thread) bars_att = settings.get_theming_attribute('thread', 'arrow_bars') heads_att = settings.get_theming_attribute('thread', 'arrow_heads') A = ArrowTree(self._tree, indent=2, childbar_offset=0, arrow_tip_att=heads_att, arrow_att=bars_att, ) self._nested_tree = NestedTree(A, interpret_covered=True) self.body = TreeBox(self._nested_tree) self.message_count = self.thread.get_total_messages()
def rebuild(self): try: self.thread.refresh() except NonexistantObjectError: self.body = urwid.SolidFill() self.message_count = 0 return self._tree = ThreadTree(self.thread) # define A to be the tree to be wrapped by a NestedTree and displayed. # We wrap the thread tree into an ArrowTree for decoration if # indentation was requested and otherwise use it as is. if self._indent_width == 0: A = self._tree else: # we want decoration. bars_att = settings.get_theming_attribute('thread', 'arrow_bars') # only add arrow heads if there is space (indent > 1). heads_char = None heads_att = None if self._indent_width > 1: heads_char = u'\u27a4' heads_att = settings.get_theming_attribute('thread', 'arrow_heads') A = ArrowTree( self._tree, indent=self._indent_width, childbar_offset=0, arrow_tip_att=heads_att, arrow_tip_char=heads_char, arrow_att=bars_att) self._nested_tree = NestedTree(A, interpret_covered=True) self.body = TreeBox(self._nested_tree) self.message_count = self.thread.get_total_messages()
class ThreadBuffer(Buffer): """displays a thread as a tree of messages""" modename = 'thread' def __init__(self, ui, thread): """ :param ui: main UI :type ui: :class:`~alot.ui.UI` :param thread: thread to display :type thread: :class:`~alot.db.Thread` """ self.thread = thread self.message_count = thread.get_total_messages() # two semaphores for auto-removal of unread tag self._auto_unread_dont_touch_mids = set([]) self._auto_unread_writing = False self.rebuild() Buffer.__init__(self, ui, self.body) def __str__(self): return '[thread] %s (%d message%s)' % (self.thread.get_subject(), self.message_count, 's' * (self.message_count > 1)) def get_info(self): info = {} info['subject'] = self.thread.get_subject() info['authors'] = self.thread.get_authors_string() info['tid'] = self.thread.get_thread_id() info['message_count'] = self.message_count return info def get_selected_thread(self): """returns the displayed :class:`~alot.db.Thread`""" return self.thread def rebuild(self): try: self.thread.refresh() except NonexistantObjectError: self.body = urwid.SolidFill() self.message_count = 0 return self._tree = ThreadTree(self.thread) bars_att = settings.get_theming_attribute('thread', 'arrow_bars') heads_att = settings.get_theming_attribute('thread', 'arrow_heads') A = ArrowTree(self._tree, indent=2, childbar_offset=0, arrow_tip_att=heads_att, arrow_att=bars_att, ) self._nested_tree = NestedTree(A, interpret_covered=True) self.body = TreeBox(self._nested_tree) self.message_count = self.thread.get_total_messages() def render(self, size, focus=False): if settings.get('auto_remove_unread'): logging.debug('Tbuffer: auto remove unread tag from msg?') msg = self.get_selected_message() mid = msg.get_message_id() focus_pos = self.body.get_focus()[1] summary_pos = (self.body.get_focus()[1][0], (0,)) cursor_on_non_summary = (focus_pos != summary_pos) if cursor_on_non_summary: if mid not in self._auto_unread_dont_touch_mids: if 'unread' in msg.get_tags(): logging.debug('Tbuffer: removing unread') def clear(): self._auto_unread_writing = False self._auto_unread_dont_touch_mids.add(mid) self._auto_unread_writing = True msg.remove_tags(['unread'], afterwards=clear) fcmd = commands.globals.FlushCommand(silent=True) self.ui.apply_command(fcmd) else: logging.debug('Tbuffer: No, msg not unread') else: logging.debug('Tbuffer: No, mid locked for autorm-unread') else: if not self._auto_unread_writing and \ mid in self._auto_unread_dont_touch_mids: self._auto_unread_dont_touch_mids.remove(mid) logging.debug('Tbuffer: No, cursor on summary') return self.body.render(size, focus) def get_selected_mid(self): """returns Message ID of focussed message""" return self.body.get_focus()[1][0] def get_selected_message_position(self): """returns position of focussed message in the thread tree""" return self._sanitize_position((self.get_selected_mid(),)) def get_selected_messagetree(self): """returns currently focussed :class:`MessageTree`""" return self._nested_tree[self.body.get_focus()[1][:1]] def get_selected_message(self): """returns focussed :class:`~alot.db.message.Message`""" return self.get_selected_messagetree()._message def get_messagetree_positions(self): """ returns a Generator to walk through all positions of :class:`MessageTree` in the :class:`ThreadTree` of this buffer. """ return [(pos,) for pos in self._tree.positions()] def messagetrees(self): """ returns a Generator of all :class:`MessageTree` in the :class:`ThreadTree` of this buffer. """ for pos in self._tree.positions(): yield self._tree[pos] def refresh(self): """refresh and flushe caches of Thread tree""" self.body.refresh() # needed for ui.get_deep_focus.. def get_focus(self): return self.body.get_focus() def set_focus(self, pos): logging.debug('setting focus to %s ' % str(pos)) self.body.set_focus(pos) def focus_first(self): """set focus to first message of thread""" self.body.set_focus(self._nested_tree.root) def focus_last(self): self.body.set_focus(next(self._nested_tree.positions(reverse=True))) def _sanitize_position(self, pos): return self._nested_tree._sanitize_position(pos, self._nested_tree._tree) def focus_selected_message(self): """focus the summary line of currently focussed message""" # move focus to summary (root of current MessageTree) self.set_focus(self.get_selected_message_position()) def focus_parent(self): """move focus to parent of currently focussed message""" mid = self.get_selected_mid() newpos = self._tree.parent_position(mid) if newpos is not None: newpos = self._sanitize_position((newpos,)) self.body.set_focus(newpos) def focus_first_reply(self): """move focus to first reply to currently focussed message""" mid = self.get_selected_mid() newpos = self._tree.first_child_position(mid) if newpos is not None: newpos = self._sanitize_position((newpos,)) self.body.set_focus(newpos) def focus_last_reply(self): """move focus to last reply to currently focussed message""" mid = self.get_selected_mid() newpos = self._tree.last_child_position(mid) if newpos is not None: newpos = self._sanitize_position((newpos,)) self.body.set_focus(newpos) def focus_next_sibling(self): """focus next sibling of currently focussed message in thread tree""" mid = self.get_selected_mid() newpos = self._tree.next_sibling_position(mid) if newpos is not None: newpos = self._sanitize_position((newpos,)) self.body.set_focus(newpos) def focus_prev_sibling(self): """ focus previous sibling of currently focussed message in thread tree """ mid = self.get_selected_mid() localroot = self._sanitize_position((mid,)) if localroot == self.get_focus()[1]: newpos = self._tree.prev_sibling_position(mid) if newpos is not None: newpos = self._sanitize_position((newpos,)) else: newpos = localroot if newpos is not None: self.body.set_focus(newpos) def focus_next(self): """focus next message in depth first order""" mid = self.get_selected_mid() newpos = self._tree.next_position(mid) if newpos is not None: newpos = self._sanitize_position((newpos,)) self.body.set_focus(newpos) def focus_prev(self): """focus previous message in depth first order""" mid = self.get_selected_mid() localroot = self._sanitize_position((mid,)) if localroot == self.get_focus()[1]: newpos = self._tree.prev_position(mid) if newpos is not None: newpos = self._sanitize_position((newpos,)) else: newpos = localroot if newpos is not None: self.body.set_focus(newpos) def focus_property(self, prop, direction): """does a walk in the given direction and focuses the first message tree that matches the given property""" newpos = self.get_selected_mid() newpos = direction(newpos) while newpos is not None: MT = self._tree[newpos] if prop(MT): newpos = self._sanitize_position((newpos,)) self.body.set_focus(newpos) break newpos = direction(newpos) def focus_next_matching(self, querystring): """focus next matching message in depth first order""" self.focus_property(lambda x: x._message.matches(querystring), self._tree.next_position) def focus_prev_matching(self, querystring): """focus previous matching message in depth first order""" self.focus_property(lambda x: x._message.matches(querystring), self._tree.prev_position) def focus_next_unfolded(self): """focus next unfolded message in depth first order""" self.focus_property(lambda x: not x.is_collapsed(x.root), self._tree.next_position) def focus_prev_unfolded(self): """focus previous unfolded message in depth first order""" self.focus_property(lambda x: not x.is_collapsed(x.root), self._tree.prev_position) def expand(self, msgpos): """expand message at given position""" MT = self._tree[msgpos] MT.expand(MT.root) def messagetree_at_position(self, pos): """get :class:`MessageTree` for given position""" return self._tree[pos[0]] def expand_all(self): """expand all messages in thread""" for MT in self.messagetrees(): MT.expand(MT.root) def collapse(self, msgpos): """collapse message at given position""" MT = self._tree[msgpos] MT.collapse(MT.root) self.focus_selected_message() def collapse_all(self): """collapse all messages in thread""" for MT in self.messagetrees(): MT.collapse(MT.root) self.focus_selected_message() def unfold_matching(self, querystring, focus_first=True): """ expand all messages that match a given querystring. :param querystring: query to match :type querystring: str :param focus_first: set the focus to the first matching message :type focus_first: bool """ first = None for MT in self.messagetrees(): msg = MT._message if msg.matches(querystring): MT.expand(MT.root) if first is None: first = (self._tree.position_of_messagetree(MT), MT.root) self.body.set_focus(first) else: MT.collapse(MT.root) self.body.refresh()