#!/usr/bin/python import curses import weakref from . import wgmultiline as multiline from . import wgtextbox as textbox from .compatibility_code import npysNPSTree as NPSTree from .npysTree import TreeData class TreeLine(textbox.TextfieldBase): def __init__(self, *args, **keywords): self._tree_real_value = None self._tree_ignore_root = None self._tree_depth = False self._tree_sibling_next = False self._tree_has_children = False self._tree_expanded = True self._tree_last_line = False self._tree_depth_next = False self.safe_depth_display = False self.show_v_lines = True super(TreeLine, self).__init__(*args, **keywords) ########################################################################## # Methods to enable this class to work with NPSTree and newer tree classes ########################################################################## def _get_content_for_display(self, vl): try: return vl.get_content_for_display() except AttributeError: return vl.getContentForDisplay() # End Compatibility Methods ########################################################################## #EXPERIMENTAL def _print(self, left_margin=0): self.left_margin = left_margin self.parent.curses_pad.bkgdset(' ',curses.A_NORMAL) self.left_margin += self._print_tree(self.relx) if self.highlight: self.parent.curses_pad.bkgdset(' ',curses.A_STANDOUT) super(TreeLine, self)._print() def _print_tree(self, real_x): if hasattr(self._tree_real_value, 'find_depth') or hasattr(self._tree_real_value, 'findDepth'): control_chars_added = 0 this_safe_depth_display = self.safe_depth_display or ((self.width // 2) + 1) if self._tree_depth_next: _tree_depth_next = self._tree_depth_next else: _tree_depth_next = 0 dp = self._tree_depth if self._tree_ignore_root: dp -= 1 if dp: # > 0: if dp < this_safe_depth_display: for i in range(dp-1): if (i < _tree_depth_next) and (not self._tree_last_line): # was i+1 < # and not (_tree_depth_next==1): if self.show_v_lines: self.parent.curses_pad.addch(self.rely, real_x, curses.ACS_VLINE, curses.A_NORMAL) if self.height > 1: for h in range(self.height-1): self.parent.curses_pad.addch(self.rely+h+1, real_x, curses.ACS_VLINE, curses.A_NORMAL) else: self.parent.curses_pad.addch(self.rely, real_x, ' ', curses.A_NORMAL) else: if self.show_v_lines: self.parent.curses_pad.addch(self.rely, real_x, curses.ACS_BTEE, curses.A_NORMAL) else: self.parent.curses_pad.addch(self.rely, real_x, ' ', curses.A_NORMAL) real_x +=1 self.parent.curses_pad.addch(self.rely, real_x, ord(' '), curses.A_NORMAL) real_x +=1 if self._tree_sibling_next or _tree_depth_next > self._tree_depth: self.parent.curses_pad.addch(self.rely, real_x, curses.ACS_LTEE, curses.A_NORMAL) if self.height > 1: for h in range(self.height-1): self.parent.curses_pad.addch(self.rely+h+1, real_x, curses.ACS_VLINE, curses.A_NORMAL) else: self.parent.curses_pad.addch(self.rely, real_x, curses.ACS_LLCORNER, curses.A_NORMAL) real_x += 1 self.parent.curses_pad.addch(self.rely, real_x, curses.ACS_HLINE, curses.A_NORMAL) real_x += 1 else: self.parent.curses_pad.addch(self.rely, real_x, curses.ACS_HLINE, curses.A_NORMAL) real_x += 1 self.parent.curses_pad.addstr(self.rely, real_x, "[ %s ]" % (str(dp)), curses.A_NORMAL) real_x += len(str(dp)) + 4 self.parent.curses_pad.addch(self.rely, real_x, curses.ACS_RTEE, curses.A_NORMAL) real_x += 1 if self._tree_has_children: if self._tree_expanded: self.parent.curses_pad.addch(self.rely, real_x, curses.ACS_TTEE, curses.A_NORMAL) if self.height > 1: for h in range(self.height-1): self.parent.curses_pad.addch(self.rely+h+1, real_x, curses.ACS_VLINE, curses.A_NORMAL) else: self.parent.curses_pad.addch(self.rely, real_x, curses.ACS_RARROW, curses.A_NORMAL) real_x +=1 else: real_x +=1 control_chars_added += real_x - self.relx margin_needed = control_chars_added + 1 else: margin_needed = 0 return margin_needed def display_value(self, vl): try: return self.safe_string(self._get_content_for_display(self._tree_real_value)) except: # Catch the times this is None. return self.safe_string(vl) class TreeLineAnnotated(TreeLine): ## Experimental. _annotate = " ? " _annotatecolor = 'CONTROL' def getAnnotationAndColor(self): # This is actually the api. Override this function to return the correct string and colour name as a tuple. self.setAnnotateString() return (self._annotate, self._annotatecolor) def setAnnotateString(self): # This was an experimental function it was the original way to set the string and annotation. self._annotate = " ? " self._annotatecolor = 'CONTROL' def annotationColor(self, real_x): # Must return the "Margin" needed before the entry begins # historical reasons. _annotation, _color = self.getAnnotationAndColor() self.parent.curses_pad.addstr(self.rely, real_x, _annotation, self.parent.theme_manager.findPair(self, _color)) return len(_annotation) def annotationNoColor(self, real_x): # Must return the "Margin" needed before the entry begins #self.parent.curses_pad.addstr(self.rely, real_x, 'xxx') #return 3 _annotation, _color = self.getAnnotationAndColor() self.parent.curses_pad.addstr(self.rely, real_x, _annotation) return len(_annotation) def _print(self): self.left_margin = 0 self.parent.curses_pad.bkgdset(' ',curses.A_NORMAL) self.left_margin += self._print_tree(self.relx) if self.do_colors(): self.left_margin += self.annotationColor(self.left_margin+self.relx) else: self.left_margin += self.annotationNoColor(self.left_margin+self.relx) if self.highlight: self.parent.curses_pad.bkgdset(' ',curses.A_STANDOUT) super(TreeLine, self)._print() class MLTree(multiline.MultiLine): # Experimental #_contained_widgets = TreeLineAnnotated _contained_widgets = TreeLine ########################################################################## # Methods to enable this class to work with NPSTree and newer tree classes ########################################################################## def _find_depth(self, vl): try: return vl.find_depth() except AttributeError: return vl.findDepth() def _has_children(self, vl): try: return vl.has_children() except AttributeError: return vl.hasChildren() def _get_content(self, vl): try: return vl.get_content() except AttributeError: return vl.getContent() def _get_ignore_root(self, vl): try: return vl.ignore_root except AttributeError: return vl.ignoreRoot def _get_tree_as_list(self, vl): try: return vl.get_tree_as_list() except AttributeError: return vl.getTreeAsList() def _walk_tree(self, root, only_expanded=True, ignore_root=True, sort=None, sort_function=None): try: return root.walk_tree(only_expanded=only_expanded, ignore_root=ignore_root, sort=sort, sort_function=sort_function) except AttributeError: return root.walkTree(onlyExpanded=only_expanded, ignoreRoot=ignore_root, sort=sort, sort_function=sort_function) # End Compatibility Methods ########################################################################## def _setMyValues(self, tree): if tree == [] or tree == None: self._myFullValues = TreeData() #NPSTree.NPSTreeData() elif not (isinstance(tree, TreeData) or isinstance(tree, NPSTree.NPSTreeData)): tree = self.convertToTree(tree) self._myFullValues = tree if not (isinstance(tree, TreeData) or isinstance(tree, NPSTree.NPSTreeData)): raise TypeError("MultiLineTree widget can only contain a TreeData or NPSTreeData object in its values attribute") else: self._myFullValues = tree def convertToTree(self, tree): "Override this function to convert a set of values to a tree." return None def resize(self): super(MLTree, self).resize() self.clearDisplayCache() self.update(clear=True) self.display() def clearDisplayCache(self): self._cached_tree = None self._cached_sort = None self._cached_tree_as_list = None def _getApparentValues(self): try: if self._cached_tree is weakref.proxy(self._myFullValues) and \ (self._cached_sort == (self._myFullValues.sort, self._myFullValues.sort_function)): return self._cached_tree_as_list except: pass self._cached_tree = weakref.proxy(self._myFullValues) self._cached_sort = (self._myFullValues.sort, self._myFullValues.sort_function) self._cached_tree_as_list = self._get_tree_as_list(self._myFullValues) return self._cached_tree_as_list def _walkMyValues(self): return self._walk_tree(self._myFullValues) def _delMyValues(self): self._myFullValues = None values = property(_getApparentValues, _setMyValues, _delMyValues) def filter_value(self, index): if self._filter in self._get_content(self.display_value(self.values[index])): return True else: return False def display_value(self, vl): return vl def set_up_handlers(self): super(MLTree, self).set_up_handlers() self.handlers.update({ ord('<'): self.h_collapse_tree, ord('>'): self.h_expand_tree, ord('['): self.h_collapse_tree, ord(']'): self.h_expand_tree, ord('{'): self.h_collapse_all, ord('}'): self.h_expand_all, ord('h'): self.h_collapse_tree, ord('l'): self.h_expand_tree, }) def _before_print_lines(self): pass def _set_line_values(self, line, value_indexer): line._tree_real_value = None line._tree_depth = False line._tree_sibling_next = False line._tree_has_children = False line._tree_expanded = False line._tree_last_line = False line._tree_depth_next = False line._tree_ignore_root = None try: line.value = self.display_value(self.values[value_indexer]) line._tree_real_value = self.values[value_indexer] line._tree_ignore_root = self._get_ignore_root(self._myFullValues) try: line._tree_depth = self._find_depth(self.values[value_indexer]) line._tree_has_children = self._has_children(self.values[value_indexer]) line._tree_expanded = self.values[value_indexer].expanded except: line._tree_depth = False line._tree_has_children = False line._tree_expanded = False try: if line._tree_depth == self._find_depth(self.values[value_indexer+1]): line._tree_sibling_next = True else: line._tree_sibling_next = False except: line._sibling_next = False line._tree_last_line = True try: line._tree_depth_next = self._find_depth(self.values[value_indexer+1]) except: line._tree_depth_next = False line.hidden = False except IndexError: self._set_line_blank(line) except TypeError: self._set_line_blank(line) def h_collapse_tree(self, ch): if self.values[self.cursor_line].expanded and self._has_children(self.values[self.cursor_line]): self.values[self.cursor_line].expanded = False else: look_for_depth = self._find_depth(self.values[self.cursor_line]) - 1 cursor_line = self.cursor_line - 1 while cursor_line >= 0: if look_for_depth == self._find_depth(self.values[cursor_line]): self.cursor_line = cursor_line self.values[cursor_line].expanded = False break else: cursor_line -= 1 self._cached_tree = None self.display() def h_expand_tree(self, ch): if not self.values[self.cursor_line].expanded: self.values[self.cursor_line].expanded = True else: for v in self._walk_tree(self.values[self.cursor_line], only_expanded=False): v.expanded = True self._cached_tree = None self.display() def h_collapse_all(self, ch): for v in self._walk_tree(self._myFullValues, only_expanded=True): v.expanded = False self._cached_tree = None self.cursor_line = 0 self.display() def h_expand_all(self, ch): for v in self._walk_tree(self._myFullValues, only_expanded=False): v.expanded = True self._cached_tree = None self.cursor_line = 0 self.display() class MLTreeAnnotated(MLTree): _contained_widgets = TreeLineAnnotated class MLTreeAction(MLTree, multiline.MultiLineAction): pass class MLTreeAnnotatedAction(MLTree, multiline.MultiLineAction): _contained_widgets = TreeLineAnnotated