import curses import os import datetime as dt import threading import blc2 from blc2.functions.audio import Audio from blc2.functions.scene import Scene from blc2.functions.chaser import Chaser from blc2.functions.chaserstep import ChaserStep from blc2.constants import SCENE from blc2.topology import Fixture from blc2.workspace import Workspace from .globals import CURSES_LOCK from .input.tabcomp import Input from .channelbank import ChannelBank from .pager import Pager def wrap_curses(f): def inner(*args, **kwargs): return curses.wrapper(lambda stdscr: f(*args, stdscr, **kwargs)) return inner __version__ = "v0.0.1" class Interface: @staticmethod def _compute_sizes(height, width): hb = height // 2 ht = height - hb wr = width // 2 wl = width - wr - 1 return ( (ht, width), (0, 0), (4, wr), (height-4, wl+1), (hb - 4, wr), (ht, wl+1), ) def _resize(self): for a, f in zip(self._compute_sizes(*self.stdscr.getmaxyx()), (self.channel_bank.set_dim, self.channel_bank.set_pos, self.input.set_dim, self.input.set_pos, self.pager.set_dim, self.pager.set_pos)): f(*a) @wrap_curses def main(self, stdscr): height, width = stdscr.getmaxyx() self.stdscr = stdscr cbd, cbp, ind, inp, pgd, pgp = self._compute_sizes(height, width) self.channel_bank = ChannelBank(*cbp, *cbd) self.input = Input(*inp, *ind) self.input.context = self.context_base self.pager = Pager(*pgp, *pgd) todisp = [ "=== Welcome to BLC2!", "=== Currently running lib %s, int %s" % (blc2.__version__, __version__), "", ] if self._w_created: todisp.append("Created a new workspace") else: todisp.append("Loaded workspace \"%s\" from \"%s\"" % (self.w.name, self.path)) todisp.append("Authored by %s, last modified at %s" % (self.w.author, self.w.modified.strftime("%Y-%m-%d %H:%M:%S"))) self.pager.display_many(todisp) self.input.main(self._resize) def base_edit(self, fid): with self.w_lock: if fid not in self.w.functions: return "No such function" f = self.w.functions[fid] if f.type != SCENE: ## FIXME return "Can only edit scenes so far" with CURSES_LOCK: self.primitive = f self.channel_bank.set_scope(f.scope) self.channel_bank.set_values(f.values.items()) self.channel_bank.title = f.type + ' "%s"' % f.name self.input.context = self.context_scene def base_delete(self, fid): with self.w_lock: if fid not in self.w.functions: return "No such function" f = self.w.functions[fid] f.delete() def base_new_scene(self, name): with self.w_lock: f = Scene(self.w, name=name) self.base_edit(f.id) def _gather_channels(self, cr): with self.w_lock: ## Gather the affected channels channels = [] for f in self.w.fixtures.values(): for s, e in cr[0]: if (s <= f.id <= e) or (e == -1 and f.id >= s): break else: continue for n, c in enumerate(f.channels, 1): for s, e in cr[1]: if (s <= n <= e) or (e == -1 and n >= s): channels.append(c) break return channels def scene_set(self, cr, v): with self.w_lock: channels = self._gather_channels(cr) ## Set the values self.primitive.update({c: v for c in channels}) ## Update the display self.channel_bank.set_scope(self.primitive.scope) if v is not None: self.channel_bank.set_values(((c, v) for c in channels)) def scene_clear(self, cr): self.scene_set(cr, None) def scene_exit(self): self.input.context = self.context_base self.primitive = None self.channel_bank.set_scope(()) self.channel_bank.title = "Channels" def base_save(self, path=None): with self.w_lock: if path is not None: self.path = path if self.path is None: return "No path set" self.w.modified = dt.datetime.now() self.w.save(self.path) return "Saved to "+self.path def base_quit(self): quit() def list_fixtures(self): with self.w_lock: td = ["FIXTURES:"] for f in sorted(self.w.fixtures.values(), key=lambda a: a.id): td.append("- %03d; %3dc: %s" % (f.id, len(f.channels), f.name)) self.pager.display_many(td, split=True) def list_functions(self, typ): with self.w_lock: td = [typ.upper()+"S:"] for f in sorted(self.w.functions.values(), key=lambda a: a.id): if f.type == typ: td.append("- %03d: %s" % (f.id, f.name)) self.pager.display_many(td, split=True) def page(self): self.pager.user_page() def page_clear(self): self.pager.clear() def __init__(self, path): ## Have to do most of the actual initialization in the main method, as curses isn't ## ready yet. self.channel_bank = None self.input = None self.pager = None self.stdscr = None self.primitive = None self.chasers = [] self.path = path self._w_created = False if path is None or not os.path.isfile(path): self.w = Workspace("", "", 0, dt.datetime.now()) self._w_created = True else: self.w = Workspace.load(path) self.w_lock = threading.RLock() self.context_base = Input.parse_context(( ("edit $num", self.base_edit), ("delete $num", self.base_delete), ("new scene $quoted_string", self.base_new_scene), ("save", self.base_save), ("save $quoted_string", self.base_save), ("list fixtures", self.list_fixtures), ("list scenes", lambda: self.list_functions(SCENE)), ("page", self.page), ("clrpage", self.page_clear), ("quit", self.base_quit), )) self.context_scene = Input.parse_context(( ("set $channel_range to $value", self.scene_set), ("reset $channel_range", self.scene_clear), ("list fixtures", self.list_fixtures), ("list scenes", lambda: self.list_functions(SCENE)), ("page", self.page), ("clrpage", self.page_clear), ("quit", self.scene_exit), ))