summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--alot/buffers.py2
-rw-r--r--alot/foreign/urwidtrees/README.md88
-rw-r--r--alot/foreign/urwidtrees/__init__.py13
-rw-r--r--alot/foreign/urwidtrees/decoration.py512
-rwxr-xr-xalot/foreign/urwidtrees/example1.py82
-rwxr-xr-xalot/foreign/urwidtrees/example2.arrows.py27
-rwxr-xr-xalot/foreign/urwidtrees/example3.collapse.py41
-rwxr-xr-xalot/foreign/urwidtrees/example4.filesystem.py127
-rwxr-xr-xalot/foreign/urwidtrees/example5.nested.py82
-rw-r--r--alot/foreign/urwidtrees/lru_cache.py141
-rw-r--r--alot/foreign/urwidtrees/nested.py366
-rw-r--r--alot/foreign/urwidtrees/widgets.py243
-rw-r--r--alot/widgets/thread.py2
-rwxr-xr-xsetup.py3
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
diff --git a/setup.py b/setup.py
index 350bda1d..a4113414 100755
--- a/setup.py
+++ b/setup.py
@@ -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)',