summaryrefslogtreecommitdiff
path: root/interface
diff options
context:
space:
mode:
authorBen Connors <benconnors@outlook.com>2019-10-26 21:57:45 -0400
committerBen Connors <benconnors@outlook.com>2019-10-26 21:57:45 -0400
commit0e845a0dca629b40166f95cf6d266181af605555 (patch)
tree9d2abdfe3ff95fda06f844128d04c6192985898f /interface
parentcce9d77ec8a412e413b799768625f407b3280eb2 (diff)
Basically finish editing functionality
- Can create, view, delete, edit audio functions o Integrated with chaser and base - Can rename functions - Can bind chaser steps to functions - Now confirms on exit
Diffstat (limited to 'interface')
-rw-r--r--interface/audioview.py109
-rw-r--r--interface/interface.py121
2 files changed, 217 insertions, 13 deletions
diff --git a/interface/audioview.py b/interface/audioview.py
new file mode 100644
index 0000000..ebfe228
--- /dev/null
+++ b/interface/audioview.py
@@ -0,0 +1,109 @@
+import curses
+import threading
+
+from .globals import CURSES_LOCK
+
+def format_time(t):
+ t = int(t/1000 + 0.5)
+ h = (t // 3600)
+ m = t % 3600
+
+ s = m % 60
+ m = m // 60
+
+ if not m and not h:
+ return "%2ds" % s
+ elif not h:
+ return "%2dm%2ds" % (m, s)
+ return "%dh%2dm%2ds" % (h, m, s)
+
+class AudioView:
+ def set_dim(self, height, width):
+ with CURSES_LOCK, self._lock:
+ if (height, width) != (self._height, self._width):
+ self.win.erase()
+ self.win.refresh()
+
+ self.win.resize(height, width)
+ self.win.redrawwin()
+
+ self._height = height
+ self._width = width
+ self._refresh()
+ self.win.refresh()
+
+ def set_pos(self, y, x):
+ with self._lock:
+ if (y, x) != (self._y, self._x):
+ with CURSES_LOCK:
+ self.win.mvwin(y, x)
+ self._put_title()
+ self.win.refresh()
+
+ @property
+ def audio(self):
+ return self._audio
+
+ @audio.setter
+ def audio(self, v):
+ with self._lock:
+ self._audio = v
+ self._refresh()
+
+ @property
+ def title(self):
+ return self._title
+
+ @title.setter
+ def title(self, value):
+ with self._lock:
+ self._title = value
+ self._put_title()
+ self.win.refresh()
+
+ @property
+ def highlight(self):
+ return self._highlight
+
+ @highlight.setter
+ def highlight(self, value):
+ with self._lock:
+ self._highlight = value
+ self._put_title()
+ self.win.refresh()
+
+ def _put_title(self):
+ self.win.border()
+ pos = min(self._width-2-len(self._title), (3*self._width)//4 - (len(self._title) // 2))
+ self.win.addstr(self._height-1, pos, self._title, curses.A_REVERSE if self._highlight else 0)
+
+ def _refresh(self):
+ with CURSES_LOCK:
+ self.win.erase()
+
+ if self._audio is not None:
+ self.win.addstr(1, 1, "Filename: "+str(self._audio.filename))
+ self.win.addstr(2, 1, " Fade in: "+str(self._audio.fade_in)+"ms")
+ self.win.addstr(3, 1, "Duration: "+str(format_time(self._audio.duration)))
+ self.win.addstr(4, 1, "Fade out: "+str(self._audio.fade_out)+"ms")
+
+ self._put_title()
+ self.win.refresh()
+
+ def __init__(self, y, x, height, width):
+ with CURSES_LOCK:
+ self.win = curses.newwin(height, width, y, x)
+ self.win.keypad(True)
+
+ self._lock = threading.RLock()
+ self._highlight = False
+ self._title = "Audio"
+ self._height = height
+ self._width = width
+ self._y = -1
+ self._x = -1
+
+ self._audio = None
+
+ self.set_pos(y, x)
+ self.set_dim(height, width)
diff --git a/interface/interface.py b/interface/interface.py
index d7d3de4..32d9060 100644
--- a/interface/interface.py
+++ b/interface/interface.py
@@ -21,6 +21,7 @@ from .channelbank import ChannelBank
from .chaserview import ChaserView
from .pager import Pager
from .dialog import askyesnocancel
+from .audioview import AudioView
def wrap_curses(f):
def inner(*args, **kwargs):
@@ -74,8 +75,8 @@ class Interface:
self.pager = Pager(*pgp, *pgd)
todisp = [
- "=== Welcome to BLC2!",
- "=== Currently running library %s, interface %s" % (blc2.__version__, __version__),
+ "Welcome to BLC2!",
+ "Currently running library %s, interface %s" % (blc2.__version__, __version__),
"",
]
@@ -91,9 +92,17 @@ class Interface:
def handle_show(self, f):
with self.w_lock:
if f.type == SCENE:
+ if not isinstance(self.channel_bank, ChannelBank):
+ (hw, yx), *_ = self._compute_sizes(*self.stdscr.getmaxyx())
+ self.channel_bank = ChannelBank(*yx, *hw)
self.channel_bank.set_scope(f.scope)
self.channel_bank.set_values(f.values.items())
- self.channel_bank.title = f.type + ' "%s"' % f.name
+ elif f.type == AUDIO:
+ if not isinstance(self.channel_bank, AudioView):
+ (hw, yx), *_ = self._compute_sizes(*self.stdscr.getmaxyx())
+ self.channel_bank = AudioView(*yx, *hw)
+ self.channel_bank.audio = f
+ self.channel_bank.title = f.type + ' "%s"' % f.name
def handle_enter(self, fid):
with self.w_lock:
@@ -128,6 +137,11 @@ class Interface:
self.current_cv = cv
self.current_cv.highlight = True
self.input.context = self.context_chaser
+ elif f.type == AUDIO:
+ self.primitive = f
+ self.handle_show(f)
+ self.channel_bank.highlight = True
+ self.input.context = self.context_audio
else:
## FIXME
return "No other types allowed yet"
@@ -173,9 +187,9 @@ class Interface:
f = self.w.functions[fid]
f.delete()
- def base_new_scene(self, name):
+ def base_new(self, name, cls):
with self.w_lock:
- f = Scene(self.w, name=name)
+ f = cls(self.w, name=name)
self.handle_enter(f.id)
def _gather_channels(self, cr):
@@ -217,6 +231,11 @@ class Interface:
self.handle_exit()
+ def audio_exit(self):
+ self.primitive = None
+
+ self.handle_exit()
+
def handle_exit(self):
for c in self.chaser_views:
c.highlight = False
@@ -252,6 +271,9 @@ class Interface:
self.input.context = self.context_chaser
self.chaser = c
else:
+ if isinstance(self.channel_bank, AudioView):
+ (hw, yx), *_ = self._compute_sizes(*self.stdscr.getmaxyx())
+ self.channel_bank = ChannelBank(*yx, *hw)
self.channel_bank.set_scope(())
self.channel_bank.title = "Channels"
@@ -271,7 +293,10 @@ class Interface:
return "Saved to "+self.path
def base_quit(self):
- quit()
+ if askyesnocancel(self.stdscr, "Really exit?"):
+ quit()
+ else:
+ self._resize()
def list_fixtures(self):
with self.w_lock:
@@ -320,12 +345,9 @@ class Interface:
if num < 1 or num > len(self.current_cv.chaser.steps):
return "Out of range"
- self.channel_bank.set_scope(())
- self.channel_bank.title = "Channels"
-
self.current_cv.selected = num - 1
s = self.current_cv.chaser.steps[num-1]
- if s.function is not None and s.function.type in (SCENE,):
+ if s.function is not None and s.function.type in (SCENE, AUDIO,):
self.handle_show(s.function)
def chaser_edit(self, num=None):
@@ -404,7 +426,7 @@ class Interface:
return "No such function"
f = self.w.functions[fid]
if f.type not in (SCENE, AUDIO, CHASER):
- raise ValueError("Invalid function")
+ return "Invalid function"
else:
f = None
@@ -429,6 +451,24 @@ class Interface:
s.name = name
self.current_cv.set_chaser(c, s.index)
+
+ def chaser_bind(self, fid):
+ with self.w_lock:
+ if self.current_cv.selected is None:
+ return "No step selected"
+ c = self.current_cv.chaser
+ s = c.steps[self.current_cv.selected]
+
+ if fid not in self.w.functions:
+ return "No such function"
+ f = self.w.functions[fid]
+ if f.type not in (SCENE, AUDIO, CHASER):
+ return "Invalid function"
+
+ s.function = f
+
+ self.current_cv.set_chaser(c, s.index)
+ self.chaser_select(s.index+1)
def chaser_move(self, a, b = None):
with self.w_lock:
@@ -453,6 +493,31 @@ class Interface:
self.current_cv.set_chaser(c, cursel.index if cursel is not None else None)
+ def primitive_rename(self, name):
+ with self.w_lock:
+ self.primitive.name = name
+ self.channel_bank.title = self.primitive.type + ' "%s"' % name
+
+ def chaser_rename_self(self, name):
+ with self.w_lock:
+ c = self.current_cv.chaser
+ c.name = name
+
+ self.current_cv.set_chaser(c, self.current_cv.selected)
+
+ def audio_fade(self, t, out=False):
+ with self.w_lock:
+ if out:
+ self.primitive.fade_out = t
+ else:
+ self.primitive.fade_in = t
+ self.channel_bank.audio = self.primitive
+
+ def audio_filename(self, fname):
+ with self.w_lock:
+ self.primitive.filename = fname
+ self.channel_bank.audio = self.primitive
+
def __init__(self, path):
## Have to do most of the actual initialization in the main method, as curses isn't
## ready yet.
@@ -482,7 +547,9 @@ class Interface:
self.context_base = Input.parse_context((
("edit $num", self.handle_enter),
("delete $num", self.base_delete),
- ("new scene $quoted_string", self.base_new_scene),
+ ("new scene $quoted_string", lambda n: self.base_new(n, Scene)),
+ ("new chaser $quoted_string", lambda n: self.base_new(n, Chaser)),
+ ("new audio $quoted_string", lambda n: self.base_new(n, Audio)),
("add $num", self.base_add),
("remove $letter", lambda n: self.base_remove(n, True)),
@@ -494,6 +561,7 @@ class Interface:
("list fixtures", self.list_fixtures),
("list scenes", lambda: self.list_functions(SCENE)),
("list chasers", lambda: self.list_functions(CHASER)),
+ ("list audio", lambda: self.list_functions(AUDIO)),
("pager page", self.page),
("pager clear", self.page_clear),
@@ -521,20 +589,29 @@ class Interface:
("delete $num", self.chaser_delete),
("append $quoted_string", lambda n: self.chaser_new(-1, n)),
- ("append $quoted_string from $num", lambda n, s: self.chaser_new(-1, n)),
+ ("append $quoted_string from $num", lambda n, s: self.chaser_new(-1, n, s)),
("append $quoted_string from new scene $quoted_string", lambda n, s: self.chaser_new_new(-1, n, s, Scene)),
+ ("append $quoted_string from new audio $quoted_string", lambda n, s: self.chaser_new_new(-1, n, s, Audio)),
("new $num $quoted_string", self.chaser_new),
("new $num $quoted_string from $num", self.chaser_new),
("new $num $quoted_string from new scene $quoted_string", lambda i, n, s: self.chaser_new_new(i, n, s, Scene)),
+ ("new $num $quoted_string from new audio $quoted_string", lambda i, n, s: self.chaser_new_new(i, n, s, Audio)),
("rename $quoted_string", self.chaser_rename),
+ ("rename chaser $quoted_string", self.chaser_rename_self),
("move $num to $num", self.chaser_move),
("move $num", self.chaser_move),
("fade in $time", self.chaser_fade),
("fade out $time", lambda t: self.chaser_fade(t, True)),
("length $time", self.chaser_duration),
("unset", self.chaser_unset),
+ ("bind $num", self.chaser_bind),
+
+ ("list fixtures", self.list_fixtures),
+ ("list scenes", lambda: self.list_functions(SCENE)),
+ ("list chasers", lambda: self.list_functions(CHASER)),
+ ("list audio", lambda: self.list_functions(AUDIO)),
("pager page", self.page),
("pager clear", self.page_clear),
@@ -552,6 +629,7 @@ class Interface:
"unset": "Unset the duration of the selected step, inheriting the step's duration from its function.",
"pager": "Control the pager. In page mode, arrow keys, page up/down, home/end, and j/k can be used to scroll, q exits.",
"move": "Move the given step to the given position",
+ "bind": "Bind the step to the given function.",
"quit": "Return to the previous mode.",
}, self.help)
@@ -562,6 +640,9 @@ class Interface:
("list fixtures", self.list_fixtures),
("list scenes", lambda: self.list_functions(SCENE)),
("list chasers", lambda: self.list_functions(CHASER)),
+ ("list audio", lambda: self.list_functions(AUDIO)),
+
+ ("rename $quoted_string", self.primitive_rename),
("pager page", self.page),
("pager clear", self.page_clear),
@@ -574,3 +655,17 @@ class Interface:
"pager": "Control the pager. In page mode, arrow keys, page up/down, home/end, and j/k can be used to scroll, q exits.",
"quit": "Return to the previous mode.",
}, self.help)
+
+ self.context_audio = Input.parse_context((
+ ("list fixtures", self.list_fixtures),
+ ("list scenes", lambda: self.list_functions(SCENE)),
+ ("list chasers", lambda: self.list_functions(CHASER)),
+
+ ("rename $quoted_string", self.primitive_rename),
+
+ ("filename $quoted_string", self.audio_filename),
+ ("fade in $time", self.audio_fade),
+ ("fade out $time", lambda t: self.audio_fade(t, True)),
+
+ ("quit", self.audio_exit),
+ ))