140 lines
4.0 KiB
Python
140 lines
4.0 KiB
Python
# Copyright 2019 (c) Anna Schumaker.
|
|
from . import notify
|
|
from . import sort
|
|
import threading
|
|
|
|
class ETree:
|
|
def __init__(self, name="", icon=""):
|
|
self.icon = icon
|
|
self.name = name
|
|
|
|
self.children = [ ]
|
|
self.child_ids = dict()
|
|
self.tree_lock = threading.Lock()
|
|
self.parent = None
|
|
self.sibling = None
|
|
self.__new_track__()
|
|
|
|
def __getstate__(self):
|
|
with self.tree_lock:
|
|
state = self.__dict__.copy()
|
|
del state["child_ids"]
|
|
del state["tree_lock"]
|
|
return state
|
|
|
|
def __insert__(self, index, child):
|
|
self.children.insert(index, child)
|
|
child.sibling = self.__nth_child__(index + 1)
|
|
child.parent = self
|
|
|
|
if (prev := self.__nth_child__(index - 1)) != None:
|
|
with prev.tree_lock:
|
|
prev.sibling = child
|
|
|
|
self.__map_child__(child)
|
|
if len(self.children) == 1:
|
|
notify.notify("first-child", self, self.get_path())
|
|
notify.notify("child-inserted", child, self.get_path() + [ index ])
|
|
return child
|
|
|
|
def __new_track__(self):
|
|
if self.new_track != ETree.new_track:
|
|
notify.register("new-track", self.new_track)
|
|
|
|
def __nth_child__(self, n):
|
|
return self.children[n] if -1 < n < len(self.children) else None
|
|
|
|
def __map_child__(self, child):
|
|
self.child_ids[id(child)] = child
|
|
self.child_ids.update(child.child_ids)
|
|
if self.parent != None:
|
|
with self.parent.tree_lock:
|
|
self.parent.__map_child__(child)
|
|
|
|
def __setstate__(self, state):
|
|
self.parent = None
|
|
self.child_ids = dict()
|
|
self.tree_lock = threading.Lock()
|
|
|
|
self.icon = state["icon"]
|
|
self.name = state["name"]
|
|
self.children = state["children"]
|
|
for child in self.children:
|
|
self.__map_child__(child)
|
|
self.parent = state["parent"]
|
|
self.sibling = state["sibling"]
|
|
self.__new_track__()
|
|
|
|
def alloc_child(self, name):
|
|
return ETree(name)
|
|
|
|
def child_index(self, child):
|
|
with self.tree_lock:
|
|
if child.parent == self:
|
|
return self.children.index(child)
|
|
return None
|
|
|
|
def get_markup(self):
|
|
return f"<big>{self.name}</big>"
|
|
|
|
def get_path(self):
|
|
if self.parent == None:
|
|
return [ ]
|
|
return self.parent.get_path() + [ self.parent.child_index(self) ]
|
|
|
|
def insert_child(self, child):
|
|
with self.tree_lock:
|
|
(index, node) = sort.bisect(self.children, sort.key(child.name))
|
|
if node == child:
|
|
return child
|
|
return self.__insert__(index, child)
|
|
|
|
def lookup(self, name):
|
|
with self.tree_lock:
|
|
(index, child) = sort.bisect(self.children, sort.key(name))
|
|
if child != None:
|
|
return child
|
|
return self.__insert__(index, self.alloc_child(name))
|
|
|
|
def lookup_byid(self, id):
|
|
return self.child_ids.get(id, None)
|
|
|
|
def lookup_path(self, path):
|
|
if path == None:
|
|
return None
|
|
if len(path) == 0:
|
|
return self
|
|
if (child := self.nth_child(path[0])) and len(path) > 1:
|
|
child = child.lookup_path(path[1:])
|
|
return child
|
|
|
|
def n_children(self):
|
|
with self.tree_lock:
|
|
return len(self.children)
|
|
|
|
def new_track(self, track):
|
|
pass
|
|
|
|
def next_child(self):
|
|
with self.tree_lock:
|
|
return self.sibling
|
|
|
|
def nth_child(self, n):
|
|
with self.tree_lock:
|
|
return self.__nth_child__(n)
|
|
|
|
def reset(self):
|
|
with self.tree_lock:
|
|
self.children.clear()
|
|
self.__new_track__()
|
|
|
|
def sort_key(self, name=None):
|
|
return sort.key(self.name)
|
|
|
|
def walk(self):
|
|
with self.tree_lock:
|
|
if self.parent != None:
|
|
yield self
|
|
for child in self.children:
|
|
yield from child.walk()
|