You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
345 lines
14 KiB
345 lines
14 KiB
import cbox
|
|
import math
|
|
import re
|
|
from gi.repository import GObject, Gdk, Gtk, GLib as glib
|
|
|
|
# Compatibility stuff
|
|
Gtk.TreeView.insert_column_with_attributes = lambda self, col, title, renderer, xarg = None, **kwargs: self.insert_column(Gtk.TreeViewColumn(title = title, cell_renderer = renderer, **kwargs), col)
|
|
|
|
def callback(cmd, cb, args):
|
|
print ("%s %s %s" % (cmd, cb, args))
|
|
|
|
def bold_label(text, halign = 1):
|
|
l = Gtk.Label()
|
|
l.set_markup("<b>%s</b>" % text)
|
|
l.set_alignment(halign, 0.5)
|
|
return l
|
|
|
|
def left_label(text):
|
|
l = Gtk.Label(text)
|
|
l.set_alignment(0, 0.5)
|
|
return l
|
|
|
|
def standard_hslider(adj):
|
|
sc = Gtk.HScale(adjustment = adj)
|
|
sc.set_size_request(160, -1)
|
|
sc.set_value_pos(Gtk.PositionType.RIGHT)
|
|
return sc
|
|
|
|
notenames = {
|
|
'c' : 0, 'c#' : 1, 'db' : 1,
|
|
'd' : 2, 'd#' : 3, 'eb' : 3,
|
|
'e' : 4, 'e#' : 5, 'fb' : 5,
|
|
'f' : 5, 'f#' : 6, 'gb' : 6,
|
|
'g' : 7, 'g#' : 8, 'ab' : 8,
|
|
'a' : 9, 'a#' : 10, 'bb' : 10,
|
|
'b' : 11, 'b#' : 0, 'cb' : 11,
|
|
}
|
|
|
|
def sfznote2value(note):
|
|
if re.match("[0-9]+$", note):
|
|
return int(rdata['key'])
|
|
else:
|
|
g = re.match("([cdefgab](#|b)?)(-?[0-9])", note)
|
|
if g is None:
|
|
raise ValueError(note)
|
|
return 12 + int(g.group(3)) * 12 + notenames[g.group(1).lower()]
|
|
|
|
class LogMapper:
|
|
def __init__(self, min, max, format = "%f"):
|
|
self.min = min
|
|
self.max = max
|
|
self.format_string = format
|
|
def map(self, value):
|
|
return float(self.min) * ((float(self.max) / self.min) ** (value / 100.0))
|
|
def unmap(self, value):
|
|
return math.log(value / float(self.min)) * 100 / math.log(float(self.max) / self.min)
|
|
def format_value(self, value):
|
|
return self.format_string % self.map(value)
|
|
|
|
|
|
freq_format = "%0.1f"
|
|
ms_format = "%0.1f ms"
|
|
lfo_freq_mapper = LogMapper(0.01, 20, "%0.2f")
|
|
env_mapper = LogMapper(0.002, 20, "%f")
|
|
filter_freq_mapper = LogMapper(20, 20000, "%0.1f Hz")
|
|
|
|
def standard_mapped_hslider(adj, mapper):
|
|
sc = Gtk.HScale(adjustment = adj)
|
|
sc.set_size_request(160, -1)
|
|
sc.set_value_pos(Gtk.PositionType.RIGHT)
|
|
sc.connect('format-value', lambda scale, value, mapper: mapper.format_value(value), mapper)
|
|
return sc
|
|
|
|
def standard_align(w, xo, yo, xs, ys):
|
|
a = Gtk.Alignment(xalign = xo, yalign = yo, xscale = xs, yscale = ys)
|
|
a.add(w)
|
|
return a
|
|
|
|
def standard_vscroll_window(width, height, content = None):
|
|
scroller = Gtk.ScrolledWindow()
|
|
scroller.set_size_request(width, height)
|
|
scroller.set_shadow_type(Gtk.ShadowType.NONE);
|
|
scroller.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
|
if content is not None:
|
|
scroller.add_with_viewport(content)
|
|
return scroller
|
|
|
|
def standard_combo(model, active_item = None, column = 0, active_item_lookup = None, lookup_column = None, width = None):
|
|
cb = Gtk.ComboBox(model = model)
|
|
if active_item_lookup is not None:
|
|
if lookup_column is None:
|
|
lookup_column = column
|
|
active_item = ls_index(model, active_item_lookup, lookup_column)
|
|
if active_item is not None:
|
|
cb.set_active(active_item)
|
|
cell = Gtk.CellRendererText()
|
|
if width is not None:
|
|
cb.set_size_request(width, -1)
|
|
cb.pack_start(cell, True)
|
|
cb.add_attribute(cell, 'text', column)
|
|
return cb
|
|
|
|
def ls_index(list_store, value, column):
|
|
for i in range(len(list_store)):
|
|
if list_store[i][column] == value:
|
|
return i
|
|
return None
|
|
|
|
def standard_filter(patterns, name):
|
|
f = Gtk.FileFilter()
|
|
for p in patterns:
|
|
f.add_pattern(p)
|
|
f.set_name(name)
|
|
return f
|
|
|
|
def checkbox_changed_bool(checkbox, vpath):
|
|
vpath.set(1 if checkbox.get_active() else 0)
|
|
|
|
def adjustment_changed_int(adjustment, vpath):
|
|
vpath.set(int(adjustment.get_value()))
|
|
|
|
def adjustment_changed_float(adjustment, vpath):
|
|
vpath.set(float(adjustment.get_value()))
|
|
|
|
def adjustment_changed_float_mapped(adjustment, vpath, mapper):
|
|
vpath.set(mapper.map(adjustment.get_value()))
|
|
|
|
def combo_value_changed(combo, vpath, value_offset = 0):
|
|
if combo.get_active() != -1:
|
|
vpath.set(value_offset + combo.get_active())
|
|
|
|
def combo_value_changed_use_column(combo, vpath, column):
|
|
if combo.get_active() != -1:
|
|
vpath.set(combo.get_model()[combo.get_active()][column])
|
|
|
|
def tree_toggle_changed_bool(renderer, tree_path, model, opath, column):
|
|
model[int(tree_path)][column] = not model[int(tree_path)][column]
|
|
cbox.do_cmd(model.make_row_item(opath, tree_path), None, [1 if model[int(tree_path)][column] else 0])
|
|
|
|
def tree_combo_changed(renderer, tree_path, new_value, model, opath, column):
|
|
new_value = renderer.get_property('model')[new_value][0]
|
|
model[int(tree_path)][column] = new_value
|
|
cbox.do_cmd(model.make_row_item(opath, tree_path), None, [new_value])
|
|
|
|
def standard_toggle_renderer(list_model, path, column):
|
|
toggle = Gtk.CellRendererToggle()
|
|
toggle.connect('toggled', tree_toggle_changed_bool, list_model, path, column)
|
|
return toggle
|
|
|
|
def standard_combo_renderer(list_model, model, path, column):
|
|
combo = Gtk.CellRendererCombo()
|
|
combo.set_property('model', model)
|
|
combo.set_property('editable', True)
|
|
combo.set_property('has-entry', False)
|
|
combo.set_property('text-column', 1)
|
|
combo.set_property('mode', Gtk.CellRendererMode.EDITABLE)
|
|
combo.connect('changed', tree_combo_changed, list_model, path, column)
|
|
return combo
|
|
|
|
def add_display_row(t, row, label, path, values, item):
|
|
t.attach(bold_label(label), 0, 1, row, row+1, Gtk.AttachOptions.SHRINK | Gtk.AttachOptions.FILL, Gtk.AttachOptions.SHRINK)
|
|
w = left_label(getattr(values, item))
|
|
t.attach(w, 1, 2, row, row+1, Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL, Gtk.AttachOptions.SHRINK)
|
|
return w
|
|
|
|
def set_timer(widget, time, func, *args):
|
|
refresh_id = glib.timeout_add(time, func, *args)
|
|
widget.connect('destroy', lambda obj, id: glib.source_remove(id), refresh_id)
|
|
|
|
def create_menu(title, items):
|
|
menuitem = Gtk.MenuItem.new_with_mnemonic(title)
|
|
if items is not None:
|
|
menu = Gtk.Menu()
|
|
menuitem.set_submenu(menu)
|
|
for label, meth in items:
|
|
mit = Gtk.MenuItem.new_with_mnemonic(label)
|
|
if meth is None:
|
|
mit.set_sensitive(False)
|
|
else:
|
|
mit.connect('activate', meth)
|
|
menu.append(mit)
|
|
return menuitem
|
|
|
|
def note_to_name(note):
|
|
if note < 0:
|
|
return "N/A"
|
|
n = note % 12
|
|
return ("C C#D D#E F F#G G#A A#B "[n * 2 : n * 2 + 2]) + str((note // 12) - 2)
|
|
|
|
#################################################################################################################################
|
|
|
|
class TableRowWidget:
|
|
def __init__(self, label, name, **kwargs):
|
|
self.label = label
|
|
self.name = name
|
|
self.kwargs = kwargs
|
|
def get_with_default(self, name, def_value):
|
|
return self.kwargs[name] if name in self.kwargs else def_value
|
|
def create_label(self):
|
|
return bold_label(self.label)
|
|
def has_attr(self, values):
|
|
if type(values) is dict:
|
|
return self.name in values
|
|
return hasattr(values, self.name)
|
|
def get_attr(self, values):
|
|
if type(values) is dict:
|
|
return values[self.name]
|
|
return getattr(values, self.name)
|
|
def get_value(self, values, vpath):
|
|
if len(vpath.args) == 1:
|
|
return self.get_attr(values)[vpath.args[0]]
|
|
return self.get_attr(values)
|
|
def add_row(self, table, row, vpath, values):
|
|
table.attach(self.create_label(), 0, 1, row, row + 1, Gtk.AttachOptions.SHRINK | Gtk.AttachOptions.FILL, Gtk.AttachOptions.SHRINK)
|
|
widget, refresher = self.create_widget(vpath)
|
|
table.attach(widget, 1, 2, row, row + 1, Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL, Gtk.AttachOptions.SHRINK)
|
|
if values is not None:
|
|
refresher(values)
|
|
return refresher
|
|
def update_sensitive(self, widget, values):
|
|
sensitive = (values is not None) and (self.get_with_default('setter', 0) is not None) and self.has_attr(values)
|
|
widget.set_sensitive(sensitive)
|
|
return sensitive
|
|
|
|
class SliderRow(TableRowWidget):
|
|
def __init__(self, label, name, minv, maxv, **kwargs):
|
|
TableRowWidget.__init__(self, label, name, **kwargs)
|
|
self.minv = minv
|
|
self.maxv = maxv
|
|
def create_widget(self, vpath):
|
|
setter = self.get_with_default('setter', adjustment_changed_float)
|
|
step = self.kwargs['step'] if 'step' in self.kwargs else 1
|
|
adj = Gtk.Adjustment(self.minv, self.minv, self.maxv, step, 6, 0)
|
|
slider = standard_hslider(adj)
|
|
if 'digits' in self.kwargs:
|
|
slider.set_digits(self.kwargs['digits'])
|
|
if setter is not None:
|
|
adj.connect("value_changed", setter, vpath.plus(self.name))
|
|
else:
|
|
slider.set_sensitive(False)
|
|
def refresher(values):
|
|
if self.update_sensitive(slider, values):
|
|
adj.set_value(self.get_value(values, vpath))
|
|
return (slider, refresher)
|
|
|
|
class IntSliderRow(SliderRow):
|
|
def __init__(self, label, name, minv, maxv, **kwargs):
|
|
SliderRow.__init__(self, label, name, minv, maxv, setter = adjustment_changed_int, **kwargs)
|
|
def create_widget(self, vpath):
|
|
(slider, refresher) = SliderRow.create_widget(self, vpath)
|
|
slider.connect('change-value', self.on_change_value)
|
|
slider.set_digits(0)
|
|
return slider, refresher
|
|
def on_change_value(self, range, scroll, value):
|
|
range.set_value(int(value))
|
|
return True
|
|
|
|
|
|
class MappedSliderRow(TableRowWidget):
|
|
def __init__(self, label, name, mapper, **kwargs):
|
|
TableRowWidget.__init__(self, label, name, **kwargs)
|
|
self.mapper = mapper
|
|
def create_widget(self, vpath):
|
|
setter = self.get_with_default('setter', adjustment_changed_float_mapped)
|
|
adj = Gtk.Adjustment(0, 0, 100, 1, 6, 0)
|
|
slider = standard_mapped_hslider(adj, self.mapper)
|
|
if setter is not None:
|
|
adj.connect("value_changed", setter, vpath.plus(self.name), self.mapper)
|
|
else:
|
|
slider.set_sensitive(False)
|
|
def refresher(values):
|
|
if self.update_sensitive(slider, values):
|
|
adj.set_value(self.mapper.unmap(self.get_value(values, vpath)))
|
|
return (slider, refresher)
|
|
|
|
class CheckBoxRow(TableRowWidget):
|
|
def create_widget(self, vpath):
|
|
widget = Gtk.CheckButton(self.label)
|
|
widget.connect("clicked", checkbox_changed_bool, vpath.plus(self.name))
|
|
def refresher(values):
|
|
if self.update_sensitive(widget, values):
|
|
widget.set_active(self.get_value(values, vpath) > 0)
|
|
return (widget, refresher)
|
|
|
|
#################################################################################################################################
|
|
|
|
class SelectObjectDialog(Gtk.Dialog):
|
|
def __init__(self, parent):
|
|
Gtk.Dialog.__init__(self, self.title, parent=parent, modal=True)
|
|
self.add_buttons(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OK, Gtk.ResponseType.OK)
|
|
self.set_default_response(Gtk.ResponseType.OK)
|
|
model = Gtk.ListStore(GObject.TYPE_STRING, GObject.TYPE_STRING, GObject.TYPE_STRING, GObject.TYPE_STRING)
|
|
|
|
self.update_model(model)
|
|
|
|
scroll = Gtk.ScrolledWindow()
|
|
scenes = Gtk.TreeView(model)
|
|
scenes.insert_column_with_attributes(0, "Name", Gtk.CellRendererText(), text=0)
|
|
scenes.insert_column_with_attributes(1, "Title", Gtk.CellRendererText(), text=3)
|
|
scenes.insert_column_with_attributes(2, "Type", Gtk.CellRendererText(), text=1)
|
|
scenes.get_column(0).set_property('min_width', 150)
|
|
scenes.get_column(1).set_property('min_width', 300)
|
|
scenes.get_column(2).set_property('min_width', 150)
|
|
scroll.add(scenes)
|
|
self.vbox.pack_start(scroll, False, False, 0)
|
|
scenes.show()
|
|
scroll.set_size_request(640, 500)
|
|
scroll.show()
|
|
self.scenes = scenes
|
|
self.scenes.connect('cursor-changed', lambda w: self.update_default_button())
|
|
self.scenes.connect('row-activated', lambda w, path, column: self.response(Gtk.ResponseType.OK))
|
|
self.scenes.set_cursor((0,), scenes.get_column(0))
|
|
self.scenes.grab_focus()
|
|
self.update_default_button()
|
|
|
|
def get_selected_object(self):
|
|
return self.scenes.get_model()[self.scenes.get_cursor()[0].get_indices()[0]]
|
|
def update_default_button(self):
|
|
self.set_response_sensitive(Gtk.ResponseType.OK, self.scenes.get_cursor()[1] is not None)
|
|
|
|
#################################################################################################################################
|
|
|
|
class SaveConfigObjectDialog(Gtk.Dialog):
|
|
def __init__(self, parent, title):
|
|
Gtk.Dialog.__init__(self, title, parent, Gtk.DialogFlags.MODAL,
|
|
(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OK, Gtk.ResponseType.OK))
|
|
self.set_default_response(Gtk.ResponseType.OK)
|
|
|
|
l = Gtk.Label()
|
|
l.set_text_with_mnemonic("_Name")
|
|
e = Gtk.Entry()
|
|
self.entry = e
|
|
e.set_activates_default(True)
|
|
e.connect('changed', self.on_entry_changed)
|
|
row = Gtk.HBox()
|
|
row.pack_start(l, False, False, 5)
|
|
row.pack_start(e, True, True, 5)
|
|
row.show_all()
|
|
self.vbox.pack_start(row, True, True, 0)
|
|
e.grab_focus()
|
|
self.on_entry_changed(e)
|
|
def get_name(self):
|
|
return self.entry.get_text()
|
|
def on_entry_changed(self, w):
|
|
self.set_response_sensitive(Gtk.ResponseType.OK, w.get_text() != '')
|
|
|