diff options
-rw-r--r-- | alot/buffers.py | 2 | ||||
-rw-r--r-- | alot/foreign/urwidtrees/README.md | 88 | ||||
-rw-r--r-- | alot/foreign/urwidtrees/__init__.py | 13 | ||||
-rw-r--r-- | alot/foreign/urwidtrees/decoration.py | 512 | ||||
-rwxr-xr-x | alot/foreign/urwidtrees/example1.py | 82 | ||||
-rwxr-xr-x | alot/foreign/urwidtrees/example2.arrows.py | 27 | ||||
-rwxr-xr-x | alot/foreign/urwidtrees/example3.collapse.py | 41 | ||||
-rwxr-xr-x | alot/foreign/urwidtrees/example4.filesystem.py | 127 | ||||
-rwxr-xr-x | alot/foreign/urwidtrees/example5.nested.py | 82 | ||||
-rw-r--r-- | alot/foreign/urwidtrees/lru_cache.py | 141 | ||||
-rw-r--r-- | alot/foreign/urwidtrees/nested.py | 366 | ||||
-rw-r--r-- | alot/foreign/urwidtrees/widgets.py | 243 | ||||
-rw-r--r-- | alot/widgets/thread.py | 2 | ||||
-rwxr-xr-x | setup.py | 3 |
14 files changed, 4 insertions, 1725 deletions
diff --git a/alot/buffers.py b/alot/buffers.py index 29cfb846..4cb3d50a 100644 --- a/alot/buffers.py +++ b/alot/buffers.py @@ -18,7 +18,7 @@ from alot.widgets.globals import AttachmentWidget from alot.widgets.bufferlist import BufferlineWidget from alot.widgets.search import ThreadlineWidget from alot.widgets.thread import ThreadTree -from alot.foreign.urwidtrees import ArrowTree, TreeBox, NestedTree +from urwidtrees import ArrowTree, TreeBox, NestedTree class Buffer(object): diff --git a/alot/foreign/urwidtrees/README.md b/alot/foreign/urwidtrees/README.md deleted file mode 100644 index b186b729..00000000 --- a/alot/foreign/urwidtrees/README.md +++ /dev/null @@ -1,88 +0,0 @@ -Urwid Tree Container API -======================== - -This is a POC implementation of a new Widget Container API for the [urwid][urwid] toolkit. -Its design goals are - -* clear separation classes that define, decorate and display trees of widgets -* representation of trees by local operations on node positions -* easy to use default implementation for simple trees -* Collapses are considered decoration - -We propose a `urwid.ListBox`-based widget that display trees where siblings grow vertically and -children horizontally. This `TreeBox` widget handles key presses to move in the tree and -collapse/expand subtrees if possible. - -The choice to define trees by overwriting local position movements allows to -easily define potentially infinite tree structures. See `example4` for how to -walk local file systems. - -The overall structure of the API contains three parts: - - -Structure ---------- - -`tree.Tree` objects define a tree structure by implementing the local movement methods - - parent_position - first_child_position - last_child_position - next_sibling_position - prev_sibling_position - -Each of which takes and returns a `position` object of arbitrary type (fixed for the Tree) -as done in urwids ListWalker API. Apart from this, a `Tree` is assumed to define a dedicated -position `tree.root` that is used as fallback initially focussed element, -and define the `__getitem__` method to return its content (usually a Widget) for a given position. - -Note that `Tree` only defines a tree structure, it does not necessarily have any decoration around -its contained Widgets. - -There is a ready made subclass called `SimpleTree` that offers the tree API for a given -nested tuple structure. If you write your own classes its a good idea to subclass `Tree` -and just overwrite the above mentioned methods as the base class already offers a number of -derivative methods. - - -Decoration ----------- - -Is done by using (subclasses of ) `decoration.DecoratedTree`. Objects of this type -wrap around a given `Tree` and themselves behave like a (possibly altered) tree. -Per default, `DecoratedTree` just passes every method on to its underlying tree. -Decoration is done *not* by overwriting `__getitem__`, but by offering two additional -methods - - get_decorated() - decorate(). - -`get_decorated(pos)` returns the (decorated) content of the original tree at the given position. -`decorate(pos, widget,..)` decorates the given widget assuming its placed at a given position. -The former is trivially based on the latter, Containers that display `Tree`s use `get_decorated` -instead of `__getitem__` when working on `DecoratedTree`s. - -The reason for this slightly odd design choice is that first it makes it easy to read -the original content of a decorated tree: You simply use `dtree[pos]`. -Secondly, this makes it possible to recursively add line decoration when nesting (decorated) Trees. - -The module `decoration` offers a few readily usable `DecoratedTree` subclasses that implement -decoration by indentation, arrow shapes and subtree collapsing: -`CollapsibleTree`, `IndentedTree`, `CollapsibleIndentedTree`, `ArrowTree` and `CollapsibleArrowTree`. -Each can be further customized by constructor parameters. - - -Containers ----------- - -`widgets.TreeBox` is essentially a `urwid.ListBox` that displays a given `Tree`. -Per default no decoration is used and the widgets of the tree are simply displayed line by line in -depth first order. `TreeBox`'s constructor accepts a `focus` parameter to specify the initially -focussed position. Internally, it uses a `TreeListWalker` to linearize the tree to a list. - -`widgets.TreeListWalker` serve as adapter between `Tree` and ListWalker APIs: -They implement the ListWalker API using the data from a given `Tree` in depth-first order. -As such, one can directly pass on a `TreeListWalker` to an `urwid.ListBox` if one doesn't want -to use tree-based focus movement or key bindings for collapsing subtrees. - -[urwid]: http://excess.org/urwid/ diff --git a/alot/foreign/urwidtrees/__init__.py b/alot/foreign/urwidtrees/__init__.py deleted file mode 100644 index ffa23356..00000000 --- a/alot/foreign/urwidtrees/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -try: - # lru_cache is part of the stdlib from v3.2 onwards - import functools.lru_cache as lru_cache -except: - # on older versions we use a backport - import lru_cache as lru_cache - -from tree import Tree, SimpleTree -from decoration import DecoratedTree, CollapsibleTree -from decoration import IndentedTree, CollapsibleIndentedTree -from decoration import ArrowTree, CollapsibleArrowTree -from nested import NestedTree -from widgets import TreeBox diff --git a/alot/foreign/urwidtrees/decoration.py b/alot/foreign/urwidtrees/decoration.py deleted file mode 100644 index 4a27c77e..00000000 --- a/alot/foreign/urwidtrees/decoration.py +++ /dev/null @@ -1,512 +0,0 @@ -# Copyright (C) 2013 Patrick Totzke <patricktotzke@gmail.com> -# This file is released under the GNU GPL, version 3 or a later revision. -from tree import Tree, SimpleTree -import urwid -import logging - -NO_SPACE_MSG = 'too little space for requested decoration' - - -class TreeDecorationError(Exception): - pass - - -class DecoratedTree(Tree): - """ - :class:`Tree` that wraps around another :class:`Tree` and allows to read - original content as well as decorated versions thereof. - """ - def __init__(self, content): - if not isinstance(content, Tree): - # do we need this? - content = SimpleTree(content) - self._tree = content - self.root = self._tree.root - - def get_decorated(self, pos): - """ - return widget that consists of the content of original tree at given - position plus its decoration. - """ - return self.decorate(pos, self[pos]) - - def decorate(self, pos, widget, is_first=True): - """ - decorate `widget` according to a position `pos` in the original tree. - setting `is_first` to False indicates that we are decorating a line - that is *part* of the (multi-line) content at this position, but not - the first part. This allows to omit incoming arrow heads for example. - """ - return widget - - # pass on everything else to the original tree. - - def parent_position(self, pos): - return self._tree.parent_position(pos) - - def first_child_position(self, pos): - return self._tree.first_child_position(pos) - - def last_child_position(self, pos): - return self._tree.last_child_position(pos) - - def next_sibling_position(self, pos): - return self._tree.next_sibling_position(pos) - - def prev_sibling_position(self, pos): - return self._tree.prev_sibling_position(pos) - - def __getitem__(self, pos): - return self._tree[pos] - - -class CollapseMixin(object): - """ - Mixin for :class:`Tree` that allows to collapse subtrees. - - This works by overwriting - :meth:`[first|last]_child_position <first_child_position>`, forcing them to - return `None` if the given position is considered collapsed. We use a - (given) callable `is_collapsed` that accepts positions and returns a - boolean to determine which node is considered collapsed. - """ - def __init__(self, is_collapsed=lambda pos: False, - **kwargs): - self._initially_collapsed = is_collapsed - self._divergent_positions = [] - - def is_collapsed(self, pos): - """checks if given position is currently collapsed""" - collapsed = self._initially_collapsed(pos) - if pos in self._divergent_positions: - collapsed = not collapsed - return collapsed - - # implement functionality by overwriting local position transformations - - # TODO: ATM this assumes we are in a wrapper: it uses self._tree. - # This is not necessarily true, for example for subclasses of SimpleTree! - # maybe define this whole class as a wrapper? - - def last_child_position(self, pos): - if self.is_collapsed(pos): - return None - return self._tree.last_child_position(pos) - - def first_child_position(self, pos): - if self.is_collapsed(pos): - return None - return self._tree.first_child_position(pos) - - def collapsible(self, pos): - return not self._tree.is_leaf(pos) - - def set_position_collapsed(self, pos, is_collapsed): - if self.collapsible(pos): - if self._initially_collapsed(pos) == is_collapsed: - if pos in self._divergent_positions: - self._divergent_positions.remove(pos) - else: - if pos not in self._divergent_positions: - self._divergent_positions.append(pos) - - def toggle_collapsed(self, pos): - self.set_position_collapsed(pos, not self.is_collapsed(pos)) - - def collapse(self, pos): - self.set_position_collapsed(pos, True) - - def collapse_all(self): - self.set_collapsed_all(True) - - def expand_all(self): - self.set_collapsed_all(False) - - def set_collapsed_all(self, is_collapsed): - self._initially_collapsed = lambda x: is_collapsed - self._divergent_positions = [] - - def expand(self, pos): - self.set_position_collapsed(pos, False) - - -class CollapseIconMixin(CollapseMixin): - """ - Mixin for :classs:`Tree` that allows to allows to collapse subtrees - and use an indicator icon in line decorations. - This Mixin adds the ability to construct collapse-icon for a - position, indicating its collapse status to :class:`CollapseMixin`. - """ - def __init__(self, - is_collapsed=lambda pos: False, - icon_collapsed_char='+', - icon_expanded_char='-', - icon_collapsed_att=None, - icon_expanded_att=None, - icon_frame_left_char='[', - icon_frame_right_char=']', - icon_frame_att=None, - icon_focussed_att=None, - **kwargs): - """TODO: docstrings""" - CollapseMixin.__init__(self, is_collapsed, **kwargs) - self._icon_collapsed_char = icon_collapsed_char - self._icon_expanded_char = icon_expanded_char - self._icon_collapsed_att = icon_collapsed_att - self._icon_expanded_att = icon_expanded_att - self._icon_frame_left_char = icon_frame_left_char - self._icon_frame_right_char = icon_frame_right_char - self._icon_frame_att = icon_frame_att - self._icon_focussed_att = icon_focussed_att - - def _construct_collapse_icon(self, pos): - width = 0 - widget = None - char = self._icon_expanded_char - charatt = self._icon_expanded_att - if self.is_collapsed(pos): - char = self._icon_collapsed_char - charatt = self._icon_collapsed_att - if char is not None: - - columns = [] - if self._icon_frame_left_char is not None: - lchar = self._icon_frame_left_char - charlen = len(lchar) - leftframe = urwid.Text((self._icon_frame_att, lchar)) - columns.append((charlen, leftframe)) - width += charlen - - # next we build out icon widget: we feed all markups to a Text, - # make it selectable (to toggle collapse) if requested - markup = (charatt, char) - widget = urwid.Text(markup) - charlen = len(char) - columns.append((charlen, widget)) - width += charlen - - if self._icon_frame_right_char is not None: - rchar = self._icon_frame_right_char - charlen = len(rchar) - rightframe = urwid.Text((self._icon_frame_att, rchar)) - columns.append((charlen, rightframe)) - width += charlen - - widget = urwid.Columns(columns) - return width, widget - - -class CollapsibleTree(CollapseMixin, DecoratedTree): - """Undecorated Tree that allows to collapse subtrees""" - def __init__(self, tree, **kwargs): - DecoratedTree.__init__(self, tree) - CollapseMixin.__init__(self, **kwargs) - - -class IndentedTree(DecoratedTree): - """Indent tree nodes according to their depth in the tree""" - def __init__(self, tree, indent=2): - """ - :param tree: tree of widgets to be displayed - :type tree: Tree - :param indent: indentation width - :type indent: int - """ - self._indent = indent - DecoratedTree.__init__(self, tree) - - def decorate(self, pos, widget, is_first=True): - line = None - indent = self._tree.depth(pos) * self._indent - cols = [(indent, urwid.SolidFill(' ')), widget] - # construct a Columns, defining all spacer as Box widgets - line = urwid.Columns(cols, box_columns=range(len(cols))[:-1]) - return line - - -class CollapsibleIndentedTree(CollapseIconMixin, IndentedTree): - """ - Indent collapsible tree nodes according to their depth in the tree and - display icons indicating collapse-status in the gaps. - """ - def __init__(self, walker, icon_offset=1, indent=4, **kwargs): - """ - :param walker: tree of widgets to be displayed - :type walker: Tree - :param indent: indentation width - :type indent: int - :param icon_offset: distance from icon to the eginning of the tree - node. - :type icon_offset: int - """ - self._icon_offset = icon_offset - IndentedTree.__init__(self, walker, indent=indent) - CollapseIconMixin.__init__(self, **kwargs) - - def decorate(self, pos, widget, is_first=True): - """ - builds a list element for given position in the tree. - It consists of the original widget taken from the Tree and some - decoration columns depending on the existence of parent and sibling - positions. The result is a urwid.Columns widget. - """ - void = urwid.SolidFill(' ') - line = None - cols = [] - depth = self._tree.depth(pos) - - # add spacer filling all but the last indent - if depth > 0: - cols.append((depth * self._indent, void)), # spacer - - # construct last indent - # TODO - iwidth, icon = self._construct_collapse_icon(pos) - available_space = self._indent - firstindent_width = self._icon_offset + iwidth - - # stop if indent is too small for this decoration - if firstindent_width > available_space: - raise TreeDecorationError(NO_SPACE_MSG) - - # add icon only for non-leafs - is_leaf = self._tree.is_leaf(pos) - if not is_leaf: - if icon is not None: - # space to the left - cols.append((available_space - firstindent_width, - urwid.SolidFill(' '))) - # icon - icon_pile = urwid.Pile([('pack', icon), void]) - cols.append((iwidth, icon_pile)) - # spacer until original widget - available_space = self._icon_offset - cols.append((available_space, urwid.SolidFill(' '))) - else: # otherwise just add another spacer - cols.append((self._indent, urwid.SolidFill(' '))) - - cols.append(widget) # original widget ] - # construct a Columns, defining all spacer as Box widgets - line = urwid.Columns(cols, box_columns=range(len(cols))[:-1]) - - return line - - -class ArrowTree(IndentedTree): - """ - Decorates the tree by indenting nodes according to their depth and using - the gaps to draw arrows indicate the tree structure. - """ - def __init__(self, walker, - indent=3, - childbar_offset=0, - arrow_hbar_char=u'\u2500', - arrow_hbar_att=None, - arrow_vbar_char=u'\u2502', - arrow_vbar_att=None, - arrow_tip_char=u'\u27a4', - arrow_tip_att=None, - arrow_att=None, - arrow_connector_tchar=u'\u251c', - arrow_connector_lchar=u'\u2514', - arrow_connector_att=None, **kwargs): - """ - :param walker: tree of widgets to be displayed - :type walker: Tree - :param indent: indentation width - :type indent: int - """ - IndentedTree.__init__(self, walker, indent) - self._childbar_offset = childbar_offset - self._arrow_hbar_char = arrow_hbar_char - self._arrow_hbar_att = arrow_hbar_att - self._arrow_vbar_char = arrow_vbar_char - self._arrow_vbar_att = arrow_vbar_att - self._arrow_connector_lchar = arrow_connector_lchar - self._arrow_connector_tchar = arrow_connector_tchar - self._arrow_connector_att = arrow_connector_att - self._arrow_tip_char = arrow_tip_char - self._arrow_tip_att = arrow_tip_att - self._arrow_att = arrow_att - - def _construct_spacer(self, pos, acc): - """ - build a spacer that occupies the horizontally indented space between - pos's parent and the root node. It will return a list of tuples to be - fed into a Columns widget. - """ - parent = self._tree.parent_position(pos) - if parent is not None: - grandparent = self._tree.parent_position(parent) - if self._indent > 0 and grandparent is not None: - parent_sib = self._tree.next_sibling_position(parent) - draw_vbar = parent_sib is not None and \ - self._arrow_vbar_char is not None - space_width = self._indent - 1 * (draw_vbar) - self._childbar_offset - if space_width > 0: - void = urwid.AttrMap(urwid.SolidFill(' '), self._arrow_att) - acc.insert(0, ((space_width, void))) - if draw_vbar: - barw = urwid.SolidFill(self._arrow_vbar_char) - bar = urwid.AttrMap(barw, self._arrow_vbar_att or - self._arrow_att) - acc.insert(0, ((1, bar))) - return self._construct_spacer(parent, acc) - else: - return acc - - def _construct_connector(self, pos): - """ - build widget to be used as "connector" bit between the vertical bar - between siblings and their respective horizontab bars leading to the - arrow tip - """ - # connector symbol, either L or |- shaped. - connectorw = None - connector = None - if self._tree.next_sibling_position(pos) is not None: # |- shaped - if self._arrow_connector_tchar is not None: - connectorw = urwid.Text(self._arrow_connector_tchar) - else: # L shaped - if self._arrow_connector_lchar is not None: - connectorw = urwid.Text(self._arrow_connector_lchar) - if connectorw is not None: - att = self._arrow_connector_att or self._arrow_att - connector = urwid.AttrMap(connectorw, att) - return connector - - def _construct_arrow_tip(self, pos): - """returns arrow tip as (width, widget)""" - arrow_tip = None - width = 0 - if self._arrow_tip_char: - txt = urwid.Text(self._arrow_tip_char) - arrow_tip = urwid.AttrMap( - txt, self._arrow_tip_att or self._arrow_att) - width = len(self._arrow_tip_char) - return width, arrow_tip - - def _construct_first_indent(self, pos): - """ - build spacer to occupy the first indentation level from pos to the - left. This is separate as it adds arrowtip and sibling connector. - """ - cols = [] - void = urwid.AttrMap(urwid.SolidFill(' '), self._arrow_att) - available_width = self._indent - - if self._tree.depth(pos) > 0: - connector = self._construct_connector(pos) - if connector is not None: - width = connector.pack()[0] - if width > available_width: - raise TreeDecorationError(NO_SPACE_MSG) - available_width -= width - if self._tree.next_sibling_position(pos) is not None: - barw = urwid.SolidFill(self._arrow_vbar_char) - below = urwid.AttrMap(barw, self._arrow_vbar_att or - self._arrow_att) - else: - below = void - # pile up connector and bar - spacer = urwid.Pile([('pack', connector), below]) - cols.append((width, spacer)) - - #arrow tip - awidth, at = self._construct_arrow_tip(pos) - if at is not None: - if awidth > available_width: - raise TreeDecorationError(NO_SPACE_MSG) - available_width -= awidth - at_spacer = urwid.Pile([('pack', at), void]) - cols.append((awidth, at_spacer)) - - # bar between connector and arrow tip - if available_width > 0: - barw = urwid.SolidFill(self._arrow_hbar_char) - bar = urwid.AttrMap( - barw, self._arrow_hbar_att or self._arrow_att) - hb_spacer = urwid.Pile([(1, bar), void]) - cols.insert(1, (available_width, hb_spacer)) - return cols - - def decorate(self, pos, widget, is_first=True): - """ - builds a list element for given position in the tree. - It consists of the original widget taken from the Tree and some - decoration columns depending on the existence of parent and sibling - positions. The result is a urwid.Culumns widget. - """ - line = None - if pos is not None: - original_widget = widget - cols = self._construct_spacer(pos, []) - - # Construct arrow leading from parent here, - # if we have a parent and indentation is turned on - if self._indent > 0: - if is_first: - indent = self._construct_first_indent(pos) - if indent is not None: - cols = cols + indent - else: - parent = self._tree.parent_position(pos) - if self._indent > 0 and parent is not None: - parent_sib = self._tree.next_sibling_position(pos) - draw_vbar = parent_sib is not None - void = urwid.AttrMap(urwid.SolidFill(' '), - self._arrow_att) - if self._childbar_offset > 0: - cols.append((self._childbar_offset, void)) - if draw_vbar: - barw = urwid.SolidFill(self._arrow_vbar_char) - bar = urwid.AttrMap( - barw, self._arrow_vbar_att or self._arrow_att) - rspace_width = self._indent - \ - 1 - self._childbar_offset - cols.append((1, bar)) - cols.append((rspace_width, void)) - else: - cols.append((self._indent, void)) - - # add the original widget for this line - cols.append(original_widget) - # construct a Columns, defining all spacer as Box widgets - line = urwid.Columns(cols, box_columns=range(len(cols))[:-1]) - return line - - -class CollapsibleArrowTree(CollapseIconMixin, ArrowTree): - """Arrow-decoration that allows collapsing subtrees""" - def __init__(self, treelistwalker, icon_offset=0, indent=5, **kwargs): - self._icon_offset = icon_offset - ArrowTree.__init__(self, treelistwalker, indent, **kwargs) - CollapseIconMixin.__init__(self, **kwargs) - - def _construct_arrow_tip(self, pos): - - cols = [] - overall_width = self._icon_offset - - if self._icon_offset > 0: - # how often we repeat the hbar_char until width icon_offset is - # reached - hbar_char_count = len(self._arrow_hbar_char) / self._icon_offset - barw = urwid.Text(self._arrow_hbar_char * hbar_char_count) - bar = urwid.AttrMap(barw, self._arrow_hbar_att or self._arrow_att) - cols.insert(1, (self._icon_offset, bar)) - - # add icon only for non-leafs - if self.collapsible(pos): - iwidth, icon = self._construct_collapse_icon(pos) - if icon is not None: - cols.insert(0, (iwidth, icon)) - overall_width += iwidth - - # get arrow tip - awidth, tip = ArrowTree._construct_arrow_tip(self, pos) - if tip is not None: - cols.append((awidth, tip)) - overall_width += awidth - - return overall_width, urwid.Columns(cols) diff --git a/alot/foreign/urwidtrees/example1.py b/alot/foreign/urwidtrees/example1.py deleted file mode 100755 index ac1d16e6..00000000 --- a/alot/foreign/urwidtrees/example1.py +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/python -# Copyright (C) 2013 Patrick Totzke <patricktotzke@gmail.com> -# This file is released under the GNU GPL, version 3 or a later revision. - -import urwid -from tree import SimpleTree -from widgets import TreeBox - - -# define some colours -palette = [ - ('body', 'black', 'light gray'), - ('focus', 'light gray', 'dark blue', 'standout'), - ('bars', 'dark blue', 'light gray', ''), - ('arrowtip', 'light blue', 'light gray', ''), - ('connectors', 'light red', 'light gray', ''), -] - -# We use selectable Text widgets for our example.. - - -class FocusableText(urwid.WidgetWrap): - """Selectable Text used for nodes in our example""" - def __init__(self, txt): - t = urwid.Text(txt) - w = urwid.AttrMap(t, 'body', 'focus') - urwid.WidgetWrap.__init__(self, w) - - def selectable(self): - return True - - def keypress(self, size, key): - return key - -# define a test tree in the format accepted by SimpleTree. Essentially, a -# tree is given as (nodewidget, [list, of, subtrees]). SimpleTree accepts -# lists of such trees. - - -def construct_example_simpletree_structure(selectable_nodes=True, children=3): - - Text = FocusableText if selectable_nodes else urwid.Text - - # define root node - tree = (Text('ROOT'), []) - - # define some children - c = g = gg = 0 # counter - for i in range(children): - subtree = (Text('Child %d' % c), []) - # and grandchildren.. - for j in range(children): - subsubtree = (Text('Grandchild %d' % g), []) - for k in range(children): - leaf = (Text('Grand Grandchild %d' % gg), None) - subsubtree[1].append(leaf) - gg += 1 # inc grand-grandchild counter - subtree[1].append(subsubtree) - g += 1 # inc grandchild counter - tree[1].append(subtree) - c += 1 - return tree - - -def construct_example_tree(selectable_nodes=True, children=2): - # define a list of tree structures to be passed on to SimpleTree - forrest = [construct_example_simpletree_structure(selectable_nodes, - children)] - - # stick out test tree into a SimpleTree and return - return SimpleTree(forrest) - -if __name__ == "__main__": - # get example tree - stree = construct_example_tree() - - # put the tree into a treebox - treebox = TreeBox(stree) - - # add some decoration - rootwidget = urwid.AttrMap(treebox, 'body') - urwid.MainLoop(rootwidget, palette).run() # go diff --git a/alot/foreign/urwidtrees/example2.arrows.py b/alot/foreign/urwidtrees/example2.arrows.py deleted file mode 100755 index 8a4f4121..00000000 --- a/alot/foreign/urwidtrees/example2.arrows.py +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/python -# Copyright (C) 2013 Patrick Totzke <patricktotzke@gmail.com> -# This file is released under the GNU GPL, version 3 or a later revision. - -from example1 import construct_example_tree, palette # example data -from decoration import ArrowTree # for Decoration -from widgets import TreeBox -import urwid - -if __name__ == "__main__": - # get example tree - stree = construct_example_tree() - # Here, we add some decoration by wrapping the tree using ArrowTree. - atree = ArrowTree(stree, - # customize at will.. - # arrow_hbar_char=u'\u2550', - # arrow_vbar_char=u'\u2551', - # arrow_tip_char=u'\u25B7', - # arrow_connector_tchar=u'\u2560', - # arrow_connector_lchar=u'\u255A', - ) - - # put the into a treebox - treebox = TreeBox(atree) - - rootwidget = urwid.AttrMap(treebox, 'body') - urwid.MainLoop(rootwidget, palette).run() # go diff --git a/alot/foreign/urwidtrees/example3.collapse.py b/alot/foreign/urwidtrees/example3.collapse.py deleted file mode 100755 index 362dd071..00000000 --- a/alot/foreign/urwidtrees/example3.collapse.py +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/python -# Copyright (C) 2013 Patrick Totzke <patricktotzke@gmail.com> -# This file is released under the GNU GPL, version 3 or a later revision. - -from example1 import construct_example_tree, palette # example data -from decoration import CollapsibleIndentedTree # for Decoration -from widgets import TreeBox -import urwid - -if __name__ == "__main__": - # get some SimpleTree - stree = construct_example_tree() - - # Use (subclasses of) the wrapper decoration.CollapsibleTree to construct a - # tree where collapsible subtrees. Apart from the original tree, these take - # a callable `is_collapsed` that defines initial collapsed-status if a - # given position. - - # We want all grandchildren collapsed initially - if_grandchild = lambda pos: stree.depth(pos) > 1 - - # We use CollapsibleIndentedTree around the original example tree. - # This uses Indentation to indicate the tree structure and squeezes in - # text-icons to indicate the collapsed status. - # Also try CollapsibleTree or CollapsibleArrowTree.. - tree = CollapsibleIndentedTree(stree, - is_collapsed=if_grandchild, - icon_focussed_att='focus', - # indent=6, - # childbar_offset=1, - # icon_frame_left_char=None, - # icon_frame_right_char=None, - # icon_expanded_char='-', - # icon_collapsed_char='+', - ) - - # put the tree into a treebox - treebox = TreeBox(tree) - - rootwidget = urwid.AttrMap(treebox, 'body') - urwid.MainLoop(rootwidget, palette).run() # go diff --git a/alot/foreign/urwidtrees/example4.filesystem.py b/alot/foreign/urwidtrees/example4.filesystem.py deleted file mode 100755 index a81107dc..00000000 --- a/alot/foreign/urwidtrees/example4.filesystem.py +++ /dev/null @@ -1,127 +0,0 @@ -#!/usr/bin/python -# Copyright (C) 2013 Patrick Totzke <patricktotzke@gmail.com> -# This file is released under the GNU GPL, version 3 or a later revision. - -import urwid -import os -from example1 import palette # example data -from widgets import TreeBox -from tree import Tree -from decoration import CollapsibleArrowTree - - -# define selectable urwid.Text widgets to display paths -class FocusableText(urwid.WidgetWrap): - """Widget to display paths lines""" - def __init__(self, txt): - t = urwid.Text(txt) - w = urwid.AttrMap(t, 'body', 'focus') - urwid.WidgetWrap.__init__(self, w) - - def selectable(self): - return True - - def keypress(self, size, key): - return key - -# define Tree that can walk your filesystem - - -class DirectoryTree(Tree): - """ - A custom Tree representing our filesystem structure. - This implementation is rather inefficient: basically every position-lookup - will call `os.listdir`.. This makes navigation in the tree quite slow. - In real life you'd want to do some caching. - - As positions we use absolute path strings. - """ - # determine dir separator and form of root node - pathsep = os.path.sep - drive, _ = os.path.splitdrive(pathsep) - - # define root node This is part of the Tree API! - root = drive + pathsep - - def __getitem__(self, pos): - return FocusableText(pos) - - # generic helper - def _list_dir(self, path): - """returns absolute paths for all entries in a directory""" - try: - elements = [os.path.join( - path, x) for x in os.listdir(path) if os.path.isdir(path)] - elements.sort() - except OSError: - elements = None - return elements - - def _get_siblings(self, pos): - """lists the parent directory of pos """ - parent = self.parent_position(pos) - siblings = [pos] - if parent is not None: - siblings = self._list_dir(parent) - return siblings - - # Tree API - def parent_position(self, pos): - parent = None - if pos != '/': - parent = os.path.split(pos)[0] - return parent - - def first_child_position(self, pos): - candidate = None - if os.path.isdir(pos): - children = self._list_dir(pos) - if children: - candidate = children[0] - return candidate - - def last_child_position(self, pos): - candidate = None - if os.path.isdir(pos): - children = self._list_dir(pos) - if children: - candidate = children[-1] - return candidate - - def next_sibling_position(self, pos): - candidate = None - siblings = self._get_siblings(pos) - myindex = siblings.index(pos) - if myindex + 1 < len(siblings): # pos is not the last entry - candidate = siblings[myindex + 1] - return candidate - - def prev_sibling_position(self, pos): - candidate = None - siblings = self._get_siblings(pos) - myindex = siblings.index(pos) - if myindex > 0: # pos is not the first entry - candidate = siblings[myindex - 1] - return candidate - -if __name__ == "__main__": - cwd = os.getcwd() # get current working directory - dtree = DirectoryTree() # get a directory walker - - # Use CollapsibleArrowTree for decoration. - # define initial collapse: - as_deep_as_cwd = lambda pos: dtree.depth(pos) >= dtree.depth(cwd) - - # We hide the usual arrow tip and use a customized collapse-icon. - decorated_tree = CollapsibleArrowTree(dtree, - is_collapsed=as_deep_as_cwd, - arrow_tip_char=None, - icon_frame_left_char=None, - icon_frame_right_char=None, - icon_collapsed_char=u'\u25B6', - icon_expanded_char=u'\u25B7',) - - # stick it into a TreeBox and use 'body' color attribute for gaps - tb = TreeBox(decorated_tree, focus=cwd) - root_widget = urwid.AttrMap(tb, 'body') - urwid.MainLoop(root_widget, palette).run() # go diff --git a/alot/foreign/urwidtrees/example5.nested.py b/alot/foreign/urwidtrees/example5.nested.py deleted file mode 100755 index d6c07659..00000000 --- a/alot/foreign/urwidtrees/example5.nested.py +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/python -# Copyright (C) 2013 Patrick Totzke <patricktotzke@gmail.com> -# This file is released under the GNU GPL, version 3 or a later revision. - -from example1 import palette, construct_example_tree # example data -from example1 import FocusableText # Selectable Text used for nodes -from widgets import TreeBox -from tree import SimpleTree -from nested import NestedTree -from decoration import ArrowTree, CollapsibleArrowTree # decoration -import urwid -import logging - - -if __name__ == "__main__": - #logging.basicConfig(filename='example.log',level=logging.DEBUG) - # Take some Arrow decorated Tree that we later stick inside another tree. - innertree = ArrowTree(construct_example_tree()) - # Some collapsible, arrow decorated tree with extra indent - anotherinnertree = CollapsibleArrowTree(construct_example_tree(), - indent=10) - - # A SimpleTree, that contains the two above - middletree = SimpleTree( - [ - (FocusableText('Middle ROOT'), - [ - (FocusableText('Mid Child One'), None), - (FocusableText('Mid Child Two'), None), - (innertree, None), - (FocusableText('Mid Child Three'), - [ - (FocusableText('Mid Grandchild One'), None), - (FocusableText('Mid Grandchild Two'), None), - ] - ), - (anotherinnertree, - # middletree defines a childnode here. This is usually - # covered by the tree 'anotherinnertree', unless the - # interepreting NestedTree's constructor gets parameter - # interpret_covered=True.. - [ - (FocusableText('XXX I\'m invisible!'), None), - - ]), - ] - ) - ] - ) # end SimpleTree constructor for middletree - # use customized arrow decoration for middle tree - middletree = ArrowTree(middletree, - arrow_hbar_char=u'\u2550', - arrow_vbar_char=u'\u2551', - arrow_tip_char=u'\u25B7', - arrow_connector_tchar=u'\u2560', - arrow_connector_lchar=u'\u255A') - - # define outmost tree - outertree = SimpleTree( - [ - (FocusableText('Outer ROOT'), - [ - (FocusableText('Child One'), None), - (middletree, None), - (FocusableText('last outer child'), None), - ] - ) - ] - ) # end SimpleTree constructor - - # add some Arrow decoration - outertree = ArrowTree(outertree) - # wrap the whole thing into a Nested Tree - outertree = NestedTree(outertree, - # show covered nodes like XXX - interpret_covered=False - ) - - # put it into a treebox and run - treebox = TreeBox(outertree) - rootwidget = urwid.AttrMap(treebox, 'body') - urwid.MainLoop(rootwidget, palette).run() # go diff --git a/alot/foreign/urwidtrees/lru_cache.py b/alot/foreign/urwidtrees/lru_cache.py deleted file mode 100644 index 1d87a485..00000000 --- a/alot/foreign/urwidtrees/lru_cache.py +++ /dev/null @@ -1,141 +0,0 @@ -# This is a backport of functools.lru_cache, which is part of the stdlib =>v3.3. -# http://code.activestate.com/recipes/578078-py26-and-py30-backport-of-python-33s-lru-cache/ - -from collections import namedtuple -from functools import update_wrapper -from threading import Lock - -_CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"]) - -def lru_cache(maxsize=100, typed=False): - """Least-recently-used cache decorator. - - If *maxsize* is set to None, the LRU features are disabled and the cache - can grow without bound. - - If *typed* is True, arguments of different types will be cached separately. - For example, f(3.0) and f(3) will be treated as distinct calls with - distinct results. - - Arguments to the cached function must be hashable. - - View the cache statistics named tuple (hits, misses, maxsize, currsize) with - f.cache_info(). Clear the cache and statistics with f.cache_clear(). - Access the underlying function with f.__wrapped__. - - See: http://en.wikipedia.org/wiki/Cache_algorithms#Least_Recently_Used - - """ - - # Users should only access the lru_cache through its public API: - # cache_info, cache_clear, and f.__wrapped__ - # The internals of the lru_cache are encapsulated for thread safety and - # to allow the implementation to change (including a possible C version). - - def decorating_function(user_function): - - cache = dict() - stats = [0, 0] # make statistics updateable non-locally - HITS, MISSES = 0, 1 # names for the stats fields - kwd_mark = (object(),) # separate positional and keyword args - cache_get = cache.get # bound method to lookup key or return None - _len = len # localize the global len() function - lock = Lock() # because linkedlist updates aren't threadsafe - root = [] # root of the circular doubly linked list - nonlocal_root = [root] # make updateable non-locally - root[:] = [root, root, None, None] # initialize by pointing to self - PREV, NEXT, KEY, RESULT = 0, 1, 2, 3 # names for the link fields - - def make_key(args, kwds, typed, tuple=tuple, sorted=sorted, type=type): - # helper function to build a cache key from positional and keyword args - key = args - if kwds: - sorted_items = tuple(sorted(kwds.items())) - key += kwd_mark + sorted_items - if typed: - key += tuple(type(v) for v in args) - if kwds: - key += tuple(type(v) for k, v in sorted_items) - return key - - if maxsize == 0: - - def wrapper(*args, **kwds): - # no caching, just do a statistics update after a successful call - result = user_function(*args, **kwds) - stats[MISSES] += 1 - return result - - elif maxsize is None: - - def wrapper(*args, **kwds): - # simple caching without ordering or size limit - key = make_key(args, kwds, typed) if kwds or typed else args - result = cache_get(key, root) # root used here as a unique not-found sentinel - if result is not root: - stats[HITS] += 1 - return result - result = user_function(*args, **kwds) - cache[key] = result - stats[MISSES] += 1 - return result - - else: - - def wrapper(*args, **kwds): - # size limited caching that tracks accesses by recency - key = make_key(args, kwds, typed) if kwds or typed else args - with lock: - link = cache_get(key) - if link is not None: - # record recent use of the key by moving it to the front of the list - root, = nonlocal_root - link_prev, link_next, key, result = link - link_prev[NEXT] = link_next - link_next[PREV] = link_prev - last = root[PREV] - last[NEXT] = root[PREV] = link - link[PREV] = last - link[NEXT] = root - stats[HITS] += 1 - return result - result = user_function(*args, **kwds) - with lock: - root = nonlocal_root[0] - if _len(cache) < maxsize: - # put result in a new link at the front of the list - last = root[PREV] - link = [last, root, key, result] - cache[key] = last[NEXT] = root[PREV] = link - else: - # use root to store the new key and result - root[KEY] = key - root[RESULT] = result - cache[key] = root - # empty the oldest link and make it the new root - root = nonlocal_root[0] = root[NEXT] - del cache[root[KEY]] - root[KEY] = None - root[RESULT] = None - stats[MISSES] += 1 - return result - - def cache_info(): - """Report cache statistics""" - with lock: - return _CacheInfo(stats[HITS], stats[MISSES], maxsize, len(cache)) - - def cache_clear(): - """Clear the cache and cache statistics""" - with lock: - cache.clear() - root = nonlocal_root[0] - root[:] = [root, root, None, None] - stats[:] = [0, 0] - - wrapper.__wrapped__ = user_function - wrapper.cache_info = cache_info - wrapper.cache_clear = cache_clear - return update_wrapper(wrapper, user_function) - - return decorating_function diff --git a/alot/foreign/urwidtrees/nested.py b/alot/foreign/urwidtrees/nested.py deleted file mode 100644 index 8d254e07..00000000 --- a/alot/foreign/urwidtrees/nested.py +++ /dev/null @@ -1,366 +0,0 @@ -# Copyright (C) 2013 Patrick Totzke <patricktotzke@gmail.com> -# This file is released under the GNU GPL, version 3 or a later revision. -from tree import Tree -from decoration import DecoratedTree, CollapseMixin - - -class NestedTree(Tree): - """ - A Tree that wraps around Trees that may contain list walkers or other - trees. The wrapped tree may contain normal widgets as well. List walkers - and subtree contents will be expanded into the tree presented by this - wrapper. - - This wrapper's positions are tuples of positions of the original and - subtrees: For example, `(X,Y,Z)` points at position Z in tree/list at - position Y in tree/list at position X in the original tree. - - NestedTree transparently behaves like a collapsible DecoratedTree. - """ - @property - def root(self): - root = (self._tree.root,) - rcontent = self._tree[self._tree.root] - if isinstance(rcontent, Tree): - root = root + (rcontent.root,) - return root - - def _sanitize_position(self, pos, tree=None): - """ - Ensure a position tuple until the result does not - point to a :class:`Tree` any more. - """ - if pos is not None: - tree = tree or self._tree - entry = self._lookup_entry(tree, pos) - if isinstance(entry, Tree): - pos = pos + self._sanitize_position((entry.root,), tree=entry) - return pos - - def __init__(self, tree, interpret_covered=False): - self._tree = tree - self._interpret_covered = interpret_covered - - def _lookup_entry(self, tree, pos): - if len(pos) == 0: - entry = tree[tree.root] - else: - entry = tree[pos[0]] - if len(pos) > 1 and isinstance(entry, Tree): - subtree = entry - entry = self._lookup_entry(subtree, pos[1:]) - return entry - - def _depth(self, tree, pos, outmost_only=True): - depth = self._tree.depth(pos[1:]) - if not outmost_only: - entry = self._tree[pos[0]] - if isinstance(entry, Tree) and len(pos) > 1: - depth += self._depth(entry, pos[1:], outmost_only=False) - return depth - - def depth(self, pos, outmost=True): - return self._depth(self._tree, pos) - - def __getitem__(self, pos): - return self._lookup_entry(self._tree, pos) - - # DecoratedTree API - def _get_decorated_entry(self, tree, pos, widget=None, is_first=True): - entry = tree[pos[0]] - if len(pos) > 1 and isinstance(entry, Tree): - subtree = entry - entry = self._get_decorated_entry( - subtree, pos[1:], widget, is_first) - else: - entry = widget or entry - if isinstance(tree, (DecoratedTree, NestedTree)): # has decorate-API - isf = len(pos) < 2 - if not isf and isinstance(tree[pos[0]], Tree): - isf = (tree[pos[0]].parent_position(pos[1]) - is None) or not is_first - entry = tree.decorate(pos[0], entry, is_first=isf) - return entry - - def get_decorated(self, pos): - return self._get_decorated_entry(self._tree, pos) - - def decorate(self, pos, widget, is_first=True): - return self._get_decorated_entry(self._tree, pos, widget, is_first) - - # Collapse API - def _get_subtree_for(self, pos): - """returns Tree that manages pos[-1]""" - res = self._tree - candidate = self._lookup_entry(self._tree, pos[:-1]) - if isinstance(candidate, Tree): - res = candidate - return res - - def collapsible(self, pos): - res = False - subtree = self._get_subtree_for(pos) - if isinstance(subtree, (CollapseMixin, NestedTree)): - res = subtree.collapsible(pos[-1]) - return res - - def is_collapsed(self, pos): - res = False - subtree = self._get_subtree_for(pos) - if isinstance(subtree, (CollapseMixin, NestedTree)): - res = subtree.is_collapsed(pos[-1]) - return res - - def toggle_collapsed(self, pos): - subtree = self._get_subtree_for(pos) - if isinstance(subtree, (CollapseMixin, NestedTree)): - subtree.toggle_collapsed(pos) - - def collapse(self, pos): - subtree = self._get_subtree_for(pos) - if isinstance(subtree, (CollapseMixin, NestedTree)): - subtree.collapse(pos[-1]) - - def collapse_all(self): - self._collapse_all(self._tree, self.root) - - def _collapse_all(self, tree, pos=None): - if pos is not None: - if isinstance(tree, (CollapseMixin, NestedTree)): - tree.expand_all() - - if len(pos) > 1: - self._collapse_all(tree[pos[0]], pos[1:]) - nextpos = tree.next_position(pos[0]) - if nextpos is not None: - nentry = tree[nextpos] - if isinstance(nentry, Tree): - self._collapse_all(nentry, (nentry.root,)) - self._collapse_all(tree, (nextpos,)) - if isinstance(tree, (CollapseMixin, NestedTree)): - tree.collapse_all() - - def expand(self, pos): - subtree = self._get_subtree_for(pos) - if isinstance(subtree, (CollapseMixin, NestedTree)): - subtree.expand(pos[-1]) - - def expand_all(self): - self._expand_all(self._tree, self.root) - - def _expand_all(self, tree, pos=None): - if pos is not None: - if isinstance(tree, (CollapseMixin, NestedTree)): - tree.expand_all() - if len(pos) > 1: - self._expand_all(tree[pos[0]], pos[1:]) - nextpos = tree.next_position(pos[0]) - if nextpos is not None: - nentry = tree[nextpos] - if isinstance(nentry, Tree): - self._expand_all(nentry, (nentry.root,)) - self._expand_all(tree, (nextpos,)) - if isinstance(tree, (CollapseMixin, NestedTree)): - tree.expand_all() - - def is_leaf(self, pos, outmost_only=False): - return self.first_child_position(pos, outmost_only) is None - - ################################################ - # Tree API - ################################################ - def parent_position(self, pos): - candidate_pos = self._parent_position(self._tree, pos) - # return sanitized path (ensure it points to content, not a subtree) - return self._sanitize_position(candidate_pos) - - def _parent_position(self, tree, pos): - candidate_pos = None - if len(pos) > 1: - # get the deepest subtree - subtree_pos = pos[:-1] - subtree = self._lookup_entry(tree, subtree_pos) - # get parent for our position in this subtree - least_pos = pos[-1] - subparent_pos = subtree.parent_position(least_pos) - if subparent_pos is not None: - # in case there is one, we are done, the position we look for - # is the path up to the subtree plus the local parent position. - candidate_pos = subtree_pos + (subparent_pos,) - else: - # otherwise we recur and look for subtree's parent in the next - # outer tree - candidate_pos = self._parent_position(self._tree, subtree_pos) - else: - # there is only one position in the path, we return its parent in - # the outmost tree - outer_parent = self._tree.parent_position(pos[0]) - if outer_parent is not None: - # result needs to be valid position (tuple of local positions) - candidate_pos = outer_parent, - return candidate_pos - - def first_child_position(self, pos, outmost_only=False): - childpos = self._first_child_position(self._tree, pos, outmost_only) - return self._sanitize_position(childpos, self._tree) - - def _first_child_position(self, tree, pos, outmost_only=False): - childpos = None - # get content at first path element in outmost tree - entry = tree[pos[0]] - if isinstance(entry, Tree) and not outmost_only and len(pos) > 1: - # this points to a tree and we don't check the outmost tree only - # recur: get first child in the subtree for remaining path - subchild = self._first_child_position(entry, pos[1:]) - if subchild is not None: - # found a childposition, re-append the path up to this subtree - childpos = (pos[0],) + subchild - return childpos - else: - # continue in the next outer tree only if we do not drop - # "covered" parts and the position path points to a parent-less - # position in the subtree. - if (entry.parent_position(pos[1]) is not None or not - self._interpret_covered): - return None - - # return the first child of the outmost tree - outerchild = tree.first_child_position(pos[0]) - if outerchild is not None: - childpos = outerchild, - return childpos - - def last_child_position(self, pos, outmost_only=False): - childpos = self._last_child_position(self._tree, pos, outmost_only) - return self._sanitize_position(childpos, self._tree) - - def _last_child_position(self, tree, pos, outmost_only=False): - childpos = None - # get content at first path element in outmost tree - entry = tree[pos[0]] - if isinstance(entry, Tree) and not outmost_only and len(pos) > 1: - # this points to a tree and we don't check the outmost tree only - - # get last child in the outmost tree if we do not drop "covered" - # parts and the position path points to a root of the subtree. - if self._interpret_covered: - if entry.parent_position(pos[1]) is None: - # return the last child of the outmost tree - outerchild = tree.last_child_position(pos[0]) - if outerchild is not None: - childpos = outerchild, - - # continue as if we have not found anything yet - if childpos is None: - # recur: get last child in the subtree for remaining path - subchild = self._last_child_position(entry, pos[1:]) - if subchild is not None: - # found a childposition, re-prepend path up to this subtree - childpos = (pos[0],) + subchild - else: - # outmost position element does not point to a tree: - # return the last child of the outmost tree - outerchild = tree.last_child_position(pos[0]) - if outerchild is not None: - childpos = outerchild, - return childpos - - def _next_sibling_position(self, tree, pos): - candidate = None - if len(pos) > 1: - # if position path does not point to position in outmost tree, - # first get the subtree as pointed out by first dimension, recur - # and check if some inner tree already returns a sibling - subtree = tree[pos[0]] - subsibling_pos = self._next_sibling_position(subtree, pos[1:]) - if subsibling_pos is not None: - # we found our sibling, prepend the path up to the subtree - candidate = pos[:1] + subsibling_pos - else: - # no deeper tree has sibling. If inner position is root node - # the sibling in the outer tree is a valid candidate - subparent = subtree.parent_position(pos[1]) - if subparent is None: - # check if outer tree defines sibling - next_sib = tree.next_sibling_position(pos[0]) - if next_sib is not None: - # it has, we found our candidate - candidate = next_sib, - # if the inner position has depth 1, then the first child - # of its parent in the outer tree can be seen as candidate for - # this position next sibling. Those live in the shadow of the - # inner tree and are hidden unless requested otherwise - elif subtree.parent_position(subparent) is None and \ - self._interpret_covered: - # we respect "covered" stuff and inner position has depth 1 - # get (possibly nested) first child in outer tree - candidate = self._first_child_position(tree, pos[:1]) - - else: - # the position path points to the outmost tree - # just return its next sibling in the outmost tree - next_sib = tree.next_sibling_position(pos[0]) - if next_sib is not None: - candidate = next_sib, - return candidate - - def next_sibling_position(self, pos): - candidate = self._next_sibling_position(self._tree, pos) - return self._sanitize_position(candidate, self._tree) - - def _prev_sibling_position(self, tree, pos): - candidate = None - if len(pos) > 1: - # if position path does not point to position in outmost tree, - # first get the subtree as pointed out by first dimension, recur - # and check if some inner tree already returns a sibling - subtree = tree[pos[0]] - subsibling_pos = self._prev_sibling_position(subtree, pos[1:]) - if subsibling_pos is not None: - # we found our sibling, prepend the path up to the subtree - candidate = pos[:1] + subsibling_pos - else: - # no deeper tree has sibling. If inner position is root node - # the sibling in the outer tree is a valid candidate - subparent = subtree.parent_position(pos[1]) - if subparent is None: - prev_sib = tree.prev_sibling_position(pos[0]) - if prev_sib is not None: - candidate = prev_sib, - return candidate - # my position could be "hidden" by being child of a - # position pointing to a Tree object (which is then unfolded). - if self._interpret_covered: - # we respect "covered" stuff: - # if parent is Tree, return last child of its (last) root - parent_pos = self._parent_position(tree, pos) - if parent_pos is not None: - parent = self._lookup_entry(self._tree, parent_pos) - if isinstance(parent, Tree): - sib = parent.last_sibling_position(parent.root) - candidate = parent.last_child_position(sib) - if candidate is not None: - candidate = parent_pos + (candidate,) - else: - # pos points to position in outmost tree - prev_sib = tree.prev_sibling_position(pos[0]) - if prev_sib is not None: - candidate = prev_sib, - # In case our new candidate points to a Tree, pick its last root node - if candidate is not None: - entry = self._lookup_entry(tree, candidate) - if isinstance(entry, Tree): - candidate = (candidate) + (entry.last_sibling_position(entry.root),) - return candidate - - def prev_sibling_position(self, pos): - candidate = self._prev_sibling_position(self._tree, pos) - return self._sanitize_position(candidate, self._tree) - - def last_decendant(self, pos): - def lastd(pos): - c = self.last_child_position(pos) - if c is not None: - c = self.last_sibling_position(c) - return c - return self._last_in_direction(pos, lastd) diff --git a/alot/foreign/urwidtrees/widgets.py b/alot/foreign/urwidtrees/widgets.py deleted file mode 100644 index 7a9d96e0..00000000 --- a/alot/foreign/urwidtrees/widgets.py +++ /dev/null @@ -1,243 +0,0 @@ -# Copyright (C) 2013 Patrick Totzke <patricktotzke@gmail.com> -# This file is released under the GNU GPL, version 3 or a later revision. - -import urwid -import logging -from urwid import WidgetWrap, ListBox -from urwid import signals -from decoration import DecoratedTree, CollapseMixin -from nested import NestedTree -from lru_cache import lru_cache - -# The following are used to check dynamically if a tree offers sub-APIs - - -def implementsDecorateAPI(tree): - """determines if given tree offers line decoration""" - return isinstance(tree, (DecoratedTree, NestedTree)) - - -def implementsCollapseAPI(tree): - """determines if given tree can collapse positions""" - res = False - if isinstance(tree, (CollapseMixin, NestedTree)): - res = True - return res - - -class TreeListWalker(urwid.ListWalker): - """ - ListWalker to walk through a class:`Tree`. - - This translates a :class:`Tree` into a :class:`urwid.ListWalker` that is - digestible by :class:`urwid.ListBox`. - It uses :meth:`Tree.[next|prev]_position <Tree.next_position>` to determine - the next/previous position in depth first order. - """ - def __init__(self, tree, focus=None): - """ - :param tree: the tree to be displayed - :type tree: Tree - :param focus: position of node to be focussed initially. - This has to be a valid position in the Tree. - It defaults to the value of `Tree.root`. - """ - self._tree = tree - self._focus = focus or tree.root - self.root = tree.root - - @lru_cache() - def __getitem__(self, pos): - """gets (possibly decorated) line widget at given position""" - if implementsDecorateAPI(self._tree): - entry = self._tree.get_decorated(pos) - else: - entry = self._tree[pos] - return entry - - def clear_cache(self): - """removes all cached lines""" - self.__getitem__.cache_clear() - - def _get(self, pos): - """looks up widget for given position; handling invalid arguments""" - res = None, None - if pos is not None: - try: - res = self[pos], pos - except (IndexError, KeyError): - pass - return res - - # List Walker API. - def get_focus(self): - return self._get(self._focus) - - def set_focus(self, pos): - self._focus = pos - - def get_next(self, pos): - return self._get(self._tree.next_position(pos)) - - def get_prev(self, pos): - return self._get(self._tree.prev_position(pos)) - - def positions(self, reverse=False): - """returns a generator that walks the tree's positions""" - return self._tree.positions(reverse) - # end of List Walker API - - -class TreeBox(WidgetWrap): - """ - A widget that displays a given :class:`Tree`. - This is essentially a :class:`ListBox` with the ability to move the focus - based on directions in the Tree and to collapse/expand subtrees if - possible. - - TreeBox interprets `left/right` as well as `page up/`page down` to move the - focus to parent/first child and next/previous sibling respectively. All - other keys are passed to the underlying ListBox. - """ - - def __init__(self, tree, focus=None): - """ - :param tree: tree of widgets to be displayed. - :type tree: Tree - :param focus: initially focussed position - """ - self._tree = tree - self._walker = TreeListWalker(tree) - self._outer_list = ListBox(self._walker) - if focus is not None: - self._outer_list.set_focus(focus) - self.__super.__init__(self._outer_list) - - # Widget API - def get_focus(self): - return self._outer_list.get_focus() - - def set_focus(self, pos): - return self._outer_list.set_focus(pos) - - def refresh(self): - self._walker.clear_cache() - signals.emit_signal(self._walker, "modified") - - def keypress(self, size, key): - key = self._outer_list.keypress(size, key) - if key in ['left', 'right', '[', ']', '-', '+', 'C', 'E', ]: - if key == 'left': - self.focus_parent() - elif key == 'right': - self.focus_first_child() - elif key == '[': - self.focus_prev_sibling() - elif key == ']': - self.focus_next_sibling() - elif key == '-': - self.collapse_focussed() - elif key == '+': - self.expand_focussed() - elif key == 'C': - self.collapse_all() - elif key == 'E': - self.expand_all() - # This is a hack around ListBox misbehaving: - # it seems impossible to set the focus without calling keypress as - # otherwise the change becomes visible only after the next render() - return self._outer_list.keypress(size, None) - else: - return self._outer_list.keypress(size, key) - - # Collapse operations - def collapse_focussed(self): - """ - Collapse currently focussed position; works only if the underlying - tree allows it. - """ - if implementsCollapseAPI(self._tree): - w, focuspos = self.get_focus() - self._tree.collapse(focuspos) - self._walker.clear_cache() - self.refresh() - - def expand_focussed(self): - """ - Expand currently focussed position; works only if the underlying - tree allows it. - """ - if implementsCollapseAPI(self._tree): - w, focuspos = self.get_focus() - self._tree.expand(focuspos) - self._walker.clear_cache() - self.refresh() - - def collapse_all(self): - """ - Collapse all positions; works only if the underlying tree allows it. - """ - if implementsCollapseAPI(self._tree): - self._tree.collapse_all() - self.set_focus(self._tree.root) - self._walker.clear_cache() - self.refresh() - - def expand_all(self): - """ - Expand all positions; works only if the underlying tree allows it. - """ - if implementsCollapseAPI(self._tree): - self._tree.expand_all() - self._walker.clear_cache() - self.refresh() - - # Tree based focus movement - def focus_parent(self): - """move focus to parent node of currently focussed one""" - w, focuspos = self.get_focus() - parent = self._tree.parent_position(focuspos) - if parent is not None: - self.set_focus(parent) - - def focus_first_child(self): - """move focus to first child of currently focussed one""" - w, focuspos = self.get_focus() - child = self._tree.first_child_position(focuspos) - if child is not None: - self.set_focus(child) - - def focus_last_child(self): - """move focus to last child of currently focussed one""" - w, focuspos = self.get_focus() - child = self._tree.last_child_position(focuspos) - if child is not None: - self.set_focus(child) - - def focus_next_sibling(self): - """move focus to next sibling of currently focussed one""" - w, focuspos = self.get_focus() - sib = self._tree.next_sibling_position(focuspos) - if sib is not None: - self.set_focus(sib) - - def focus_prev_sibling(self): - """move focus to previous sibling of currently focussed one""" - w, focuspos = self.get_focus() - sib = self._tree.prev_sibling_position(focuspos) - if sib is not None: - self.set_focus(sib) - - def focus_next(self): - """move focus to next position (DFO)""" - w, focuspos = self.get_focus() - next = self._tree.next_position(focuspos) - if next is not None: - self.set_focus(next) - - def focus_prev(self): - """move focus to previous position (DFO)""" - w, focuspos = self.get_focus() - prev = self._tree.prev_position(focuspos) - if prev is not None: - self.set_focus(prev) diff --git a/alot/widgets/thread.py b/alot/widgets/thread.py index 24d63303..c49bbb9d 100644 --- a/alot/widgets/thread.py +++ b/alot/widgets/thread.py @@ -12,7 +12,7 @@ from alot.db.utils import decode_header, X_SIGNATURE_MESSAGE_HEADER from alot.helper import tag_cmp from alot.widgets.globals import TagWidget from alot.widgets.globals import AttachmentWidget -from alot.foreign.urwidtrees import Tree, SimpleTree, CollapsibleTree +from urwidtrees import Tree, SimpleTree, CollapsibleTree from alot.db.utils import extract_body @@ -19,7 +19,7 @@ setup(name='alot', 'alot.utils', 'alot.widgets', 'alot.foreign', - 'alot.foreign.urwidtrees'], + ], package_data={ 'alot': [ 'defaults/alot.rc.spec', @@ -35,6 +35,7 @@ setup(name='alot', 'notmuch (>=0.13)', 'argparse (>=2.7)', 'urwid (>=1.1.0)', + 'urwidtrees (>=1.0)', 'twisted (>=10.2.0)', 'magic', 'configobj (>=4.6.0)', |