Fix parsing of log files
[laditools/alessio.git] / ladiconf
blob3dec969fa2aefcb3790d4767272fe139804b2fc8
1 #!/usr/bin/env python
3 # LADITools - Linux Audio Desktop Integration Tools
4 # ladiconf - A configuration GUI for your Linux Audio Desktop
5 # Copyright (C) 2007-2010, Marc-Olivier Barre <marco@marcochapeau.org>
6 # Copyright (C) 2007-2009, Nedko Arnaudov <nedko@arnaudov.name>
7 # Copyright (C) 2008, Krzysztof Foltman <wdev@foltman.com>
9 # This program is free software: you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation, either version 3 of the License, or
12 # (at your option) any later version.
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with this program. If not, see <http://www.gnu.org/licenses/>.
22 import pygtk
23 pygtk.require ('2.0')
24 import gtk
25 import laditools
26 import gobject
27 import sys
29 jack = laditools.jack_configure()
31 def check_ladish():
32 try:
33 proxy = laditools.ladish_proxy()
34 except:
35 print "ladish proxy creation failed"
36 return
38 if not proxy.is_available():
39 print "ladish is not available"
40 return
42 if proxy.studio_is_loaded():
43 if not proxy.studio_is_started():
44 print "ladish studio is loaded and not started"
45 return
46 else:
47 msg = "JACK can only be configured with a stopped studio. Please stop your studio first."
48 title = "Studio is running"
49 else:
50 msg = "JACK can only be configured with a loaded and stopped studio. Please create a new studio or load and stop an existing one."
51 title = "No studio present"
53 # studio is not loaded or loaded and started
54 mdlg = gtk.MessageDialog(type = gtk.MESSAGE_ERROR, buttons = gtk.BUTTONS_CLOSE, message_format = msg)
55 mdlg.set_title(title)
56 mdlg.run()
57 mdlg.hide()
58 sys.exit(0)
60 class parameter:
61 def __init__(self, path):
62 self.path = path
63 self.name = path[-1:]
65 def get_name(self):
66 return self.name
68 def get_type(self):
69 return jack.get_param_type(self.path)
71 def get_value(self):
72 return jack.get_param_value(self.path)
74 def set_value(self, value):
75 jack.set_param_value(self.path, value)
77 def reset_value(self):
78 jack.reset_param_value(self.path)
80 def get_short_description(self):
81 return jack.get_param_short_description(self.path)
83 def get_long_description(self):
84 descr = jack.get_param_long_description(self.path)
85 if not descr:
86 descr = self.get_short_description()
87 return descr
89 def has_range(self):
90 return jack.param_has_range(self.path)
92 def get_range(self):
93 return jack.param_get_range(self.path)
95 def has_enum(self):
96 return jack.param_has_enum(self.path)
98 def is_strict_enum(self):
99 return jack.param_is_strict_enum(self.path)
101 def is_fake_values_enum(self):
102 return jack.param_is_fake_value(self.path)
104 def get_enum_values(self):
105 return jack.param_get_enum_values(self.path)
107 class configure_command:
108 def __init__(self):
109 pass
111 def get_description(self):
112 pass
114 def get_window_title(self):
115 return self.get_description();
117 def run(self, arg):
118 pass
120 class parameter_enum_value(gobject.GObject):
121 def __init__(self, is_fake_value, value, description):
122 gobject.GObject.__init__(self)
123 self.is_fake_value = is_fake_value
124 self.value = value
125 self.description = description
127 def get_description(self):
128 if self.is_fake_value:
129 return self.description
131 return str(self.value) + " - " + self.description
133 gobject.type_register(parameter_enum_value)
135 class parameter_store(gobject.GObject):
136 def __init__(self, param):
137 gobject.GObject.__init__(self)
138 self.param = param
139 self.name = self.param.get_name()
140 self.is_set, self.default_value, self.value = self.param.get_value()
141 self.modified = False
142 self.has_range = self.param.has_range()
143 self.is_strict = self.param.is_strict_enum()
144 self.is_fake_value = self.param.is_fake_values_enum()
146 self.enum_values = []
148 if self.has_range:
149 self.range_min, self.range_max = self.param.get_range()
150 else:
151 for enum_value in self.param.get_enum_values():
152 self.enum_values.append(parameter_enum_value(self.is_fake_value, enum_value[0], enum_value[1]))
154 def get_name(self):
155 return self.name
157 def get_type(self):
158 return self.param.get_type()
160 def get_value(self):
161 return self.value
163 def get_default_value(self):
164 if not self.is_fake_value:
165 return str(self.default_value)
167 for enum_value in self.get_enum_values():
168 if enum_value.value == self.default_value:
169 return enum_value.get_description()
171 return "???"
173 def set_value(self, value):
174 #print "%s -> %s" % (self.name, value)
175 self.value = value
176 self.modified = True
178 def reset_value(self):
179 self.value = self.default_value
181 def get_short_description(self):
182 return self.param.get_short_description()
184 def maybe_save_value(self):
185 if self.modified:
186 self.param.set_value(self.value)
187 self.modified = False
189 def get_range(self):
190 return self.range_min, self.range_max
192 def has_enum(self):
193 return len(self.enum_values) != 0
195 def is_strict_enum(self):
196 return self.is_strict
198 def get_enum_values(self):
199 return self.enum_values
201 gobject.type_register(parameter_store)
203 def combobox_get_active_text(combobox, model_index = 0):
204 model = combobox.get_model()
205 active = combobox.get_active()
206 if active < 0:
207 return None
208 return model[active][model_index]
210 class cell_renderer_param(gtk.GenericCellRenderer):
211 __gproperties__ = { "parameter": (gobject.TYPE_OBJECT, "Parameter", "Parameter", gobject.PARAM_READWRITE) }
213 def __init__(self):
214 self.__gobject_init__()
215 self.parameter = None
216 #self.set_property('mode', gtk.CELL_RENDERER_MODE_EDITABLE)
217 #self.set_property('mode', gtk.CELL_RENDERER_MODE_ACTIVATABLE)
218 self.renderer_text = gtk.CellRendererText()
219 self.renderer_toggle = gtk.CellRendererToggle()
220 self.renderer_combo = gtk.CellRendererCombo()
221 self.renderer_spinbutton = gtk.CellRendererSpin()
222 for r in (self.renderer_text, self.renderer_combo, self.renderer_spinbutton):
223 r.connect("edited", self.on_edited)
224 self.renderer = None
225 self.edit_widget = None
227 def do_set_property(self, pspec, value):
228 if pspec.name == 'parameter':
229 if value.get_type() == 'b':
230 self.set_property('mode', gtk.CELL_RENDERER_MODE_ACTIVATABLE)
231 else:
232 self.set_property('mode', gtk.CELL_RENDERER_MODE_EDITABLE)
233 else:
234 print pspec.name
235 setattr(self, pspec.name, value)
237 def do_get_property(self, pspec):
238 return getattr(self, pspec.name)
240 def choose_renderer(self):
241 typechar = self.parameter.get_type()
242 value = self.parameter.get_value()
244 if typechar == "b":
245 self.renderer = self.renderer_toggle
246 self.renderer.set_property('activatable', True)
247 self.renderer.set_property('active', value)
248 self.renderer.set_property("xalign", 0.0)
249 return
251 if self.parameter.has_enum():
252 self.renderer = self.renderer_combo
254 m = gtk.ListStore(str, parameter_enum_value)
256 for value in self.parameter.get_enum_values():
257 m.append([value.get_description(), value])
259 self.renderer.set_property("model",m)
260 self.renderer.set_property('text-column', 0)
261 self.renderer.set_property('editable', True)
262 self.renderer.set_property('has_entry', not self.parameter.is_strict_enum())
264 value = self.parameter.get_value()
265 if self.parameter.is_fake_value:
266 text = "???"
267 for enum_value in self.parameter.get_enum_values():
268 if enum_value.value == value:
269 text = enum_value.get_description()
270 break
271 else:
272 text = str(value)
274 self.renderer.set_property('text', text)
276 return
278 if typechar == 'u' or typechar == 'i':
279 self.renderer = self.renderer_spinbutton
280 self.renderer.set_property('text', str(value))
281 self.renderer.set_property('editable', True)
282 if self.parameter.has_range:
283 range_min, range_max = self.parameter.get_range()
284 self.renderer.set_property('adjustment', gtk.Adjustment(value, range_min, range_max, 1, abs(int((range_max - range_min) / 10))))
285 else:
286 self.renderer.set_property('adjustment', gtk.Adjustment(value, 0, 100000, 1, 1000))
287 return
289 self.renderer = self.renderer_text
290 self.renderer.set_property('editable', True)
291 self.renderer.set_property('text', self.parameter.get_value())
293 def on_render(self, window, widget, background_area, cell_area, expose_area, flags):
294 self.choose_renderer()
295 return self.renderer.render(window, widget, background_area, cell_area, expose_area, flags)
297 def on_get_size(self, widget, cell_area=None):
298 self.choose_renderer()
299 return self.renderer.get_size(widget, cell_area)
301 def on_activate(self, event, widget, path, background_area, cell_area, flags):
302 self.choose_renderer()
303 if self.parameter.get_type() == 'b':
304 self.parameter.set_value(not self.parameter.get_value())
305 widget.get_model()[path][4] = "modified"
306 return True
308 def on_edited(self, renderer, path, value_str):
309 parameter = self.edit_parameter
310 widget = self.edit_widget
311 model = self.edit_tree.get_model()
312 self.edit_widget = None
313 typechar = parameter.get_type()
314 if type(widget) == gtk.ComboBox:
315 value = combobox_get_active_text(widget, 1)
316 if value == None:
317 return
318 value_str = value.value
319 elif type(widget) == gtk.ComboBoxEntry:
320 enum_value = combobox_get_active_text(widget, 1)
321 if enum_value:
322 value_str = enum_value.value
323 else:
324 value_str = widget.get_active_text()
326 if typechar == 'u' or typechar == 'i':
327 try:
328 value = int(value_str)
329 except ValueError, e:
330 # Hide the widget (because it may display something else than what user typed in)
331 widget.hide()
332 # Display the error
333 mdlg = gtk.MessageDialog(buttons = gtk.BUTTONS_OK, message_format = "Invalid value. Please enter an integer number.")
334 mdlg.run()
335 mdlg.hide()
336 # Return the focus back to the tree to prevent buttons from stealing it
337 self.edit_tree.grab_focus()
338 return
339 else:
340 value = value_str
341 parameter.set_value(value)
342 model[path][4] = "modified"
343 self.edit_tree.grab_focus()
345 def on_start_editing(self, event, widget, path, background_area, cell_area, flags):
346 # this happens when edit requested using keyboard
347 if not event:
348 event = gtk.gdk.Event(gtk.gdk.NOTHING)
350 self.choose_renderer()
351 ret = self.renderer.start_editing(event, widget, path, background_area, cell_area, flags)
352 self.edit_widget = ret
353 self.edit_tree = widget
354 self.edit_parameter = self.parameter
355 return ret
357 gobject.type_register(cell_renderer_param)
359 class ladiconf_tooltips(laditools.TreeViewTooltips):
360 def __init__(self, name_column, is_set_column):
361 self.name_column = name_column
362 self.is_set_column = is_set_column
363 laditools.TreeViewTooltips.__init__(self)
365 def location(self, x, y, w, h):
366 return x + 10, y + 10
368 def get_tooltip(self, view, column, path):
369 if column is self.name_column:
370 model = view.get_model()
371 tooltip = model[path][3]
372 return tooltip
374 if column is self.is_set_column:
375 model = view.get_model()
376 param = model[path][1]
377 is_set = model[path][4]
379 if is_set == "default":
380 return None
382 if is_set == "reset":
383 return "Value will be reset to %s" % param.get_default_value()
385 return "Double-click to schedule reset of value to %s" % param.get_default_value()
387 return None
389 class jack_params_configure_command(configure_command):
390 def __init__(self, path):
391 self.path = path
392 self.is_setting = False
394 def reset_value(self, row_path):
395 row = self.liststore[row_path]
396 param = row[1]
397 param.reset_value()
398 row[4] = "reset"
400 def on_row_activated(self, treeview, path, view_column):
401 if view_column == self.tvcolumn_is_set:
402 self.reset_value(path)
404 def on_button_press_event(self, tree, event):
405 if event.type != gtk.gdk._2BUTTON_PRESS:
406 return False
407 # this is needed for proper double-click handling in the list; don't ask me why, I don't know
408 # it's probably because _2BUTTON_PRESS event is still delivered to tree view, automatically deactivating
409 # the newly created edit widget (which gets created on second BUTTON_PRESS but before _2BUTTON_PRESS)
410 # deactivating the widget causes it to be deleted
411 return True
413 def on_key_press_event(self, tree, event):
414 cur = self.treeview.get_cursor()
415 row_path = cur[0][0]
417 # if Delete was pressed, reset the value
418 if event.state == 0 and event.keyval == gtk.keysyms.Delete:
419 self.reset_value(row_path)
420 tree.queue_draw()
421 return True
423 # prevent ESC from activating the editor
424 if event.string < " ":
425 return False
427 # single-key data entry: if the control is a text entry, spin button or combo box/combo box entry,
428 # then edit the current and set the text value to what user has already typed in
429 cur = self.treeview.get_cursor()
430 param = self.liststore[row_path][1]
431 ptype = param.get_type()
433 # we don't care about booleans
434 if ptype == 'b':
435 return False
437 # accept only digits for integer input (or a minus, but only if it's a signed field)
438 if ptype in ('i', 'u'):
439 if not (event.string.isdigit() or (event.string == "-" and ptype == 'i')):
440 return False
442 # Start cell editing
443 # MAYBE: call a specially crafted cell_renderer_param method for this
444 self.treeview.set_cursor_on_cell(cur[0], self.tvcolumn_value, self.renderer_value.renderer, True)
446 # cell_renderer_param::on_start_editing() should set edit_widget.
447 # if edit operation has failed (didn't create a widget), pass the key on
448 # MAYBE: call a specially crafted cell_renderer_param method to do this check
449 if self.renderer_value.edit_widget == None:
450 return
452 widget = self.renderer_value.edit_widget
453 if type(widget) in (gtk.Entry, gtk.ComboBoxEntry):
454 # (combo or plain) text entry - set the content and move the cursor to the end
455 sl = len(event.string)
456 widget.set_text(event.string)
457 widget.select_region(sl, sl)
458 return True
460 if type(self.renderer_value.edit_widget) == gtk.SpinButton:
461 # spin button - set the value and move the cursor to the end
462 if event.string == "-":
463 # special case for minus sign (which can't be converted to float)
464 widget.set_text(event.string)
465 else:
466 widget.set_value(float(event.string))
467 sl = len(widget.get_text())
468 widget.select_region(sl, sl)
469 return True
471 if type(self.renderer_value.edit_widget) == gtk.ComboBox:
472 # combo box - select the first item that starts with typed character
473 model = widget.get_model()
474 item = -1
475 iter = model.get_iter_root()
476 while iter != None:
477 if model.get_value(iter, 0).startswith(event.string):
478 item = model.get_path(iter)[0]
479 break
480 iter = model.iter_next(iter)
481 widget.set_active(item)
482 return True
484 return False
486 def on_cursor_changed(self, tree):
487 cur = self.treeview.get_cursor()
488 if not self.is_setting and cur[1] != None and cur[1].get_title() != self.tvcolumn_value.get_title():
489 self.is_setting = True
490 try:
491 self.treeview.set_cursor_on_cell(cur[0], self.tvcolumn_value)
492 finally:
493 self.is_setting = False
495 def ok_clicked(self, dlg):
496 if self.renderer_value.edit_widget:
497 self.renderer_value.edit_widget.editing_done()
499 def run(self, arg):
500 dlg = gtk.Dialog()
501 dlg.set_title(self.get_window_title())
502 dlg.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
503 dlg.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK).connect("clicked", self.ok_clicked)
505 #dlg.set_transient_for(window)
507 self.liststore = gtk.ListStore(str, parameter_store, str, str, str)
508 self.treeview = gtk.TreeView(self.liststore)
509 self.treeview.set_rules_hint(True)
510 self.treeview.set_enable_search(False)
512 renderer_text = gtk.CellRendererText()
513 renderer_toggle = gtk.CellRendererToggle()
514 renderer_value = cell_renderer_param()
515 self.renderer_value = renderer_value # save for use in event handler methods
517 self.tvcolumn_parameter = gtk.TreeViewColumn('Parameter', renderer_text, text=0)
518 self.tvcolumn_is_set = gtk.TreeViewColumn('Status', renderer_text, text=4)
519 self.tvcolumn_value = gtk.TreeViewColumn('Value', renderer_value, parameter=1)
520 self.tvcolumn_description = gtk.TreeViewColumn('Description', renderer_text, text=2)
522 self.tvcolumn_value.set_resizable(True)
523 self.tvcolumn_value.set_min_width(100)
525 self.treeview.append_column(self.tvcolumn_parameter)
526 self.treeview.append_column(self.tvcolumn_is_set)
527 self.treeview.append_column(self.tvcolumn_value)
528 self.treeview.append_column(self.tvcolumn_description)
530 dlg.vbox.pack_start(self.treeview, True)
532 param_names = jack.get_param_names(self.path)
533 for name in param_names:
534 param = parameter(self.path + [name])
535 store = parameter_store(param)
536 if store.is_set:
537 is_set = "set"
538 else:
539 is_set = "default"
540 self.liststore.append([name, store, param.get_short_description(), param.get_long_description(), is_set])
542 self.treeview.connect("row-activated", self.on_row_activated)
543 self.treeview.connect("cursor-changed", self.on_cursor_changed)
544 self.treeview.connect("key-press-event", self.on_key_press_event)
545 self.treeview.connect("button-press-event", self.on_button_press_event)
547 self.tooltips = ladiconf_tooltips(self.tvcolumn_parameter, self.tvcolumn_is_set)
548 self.tooltips.add_view(self.treeview)
549 if len(param_names):
550 # move cursor to first row and 'value' column
551 self.treeview.set_cursor((0,), self.tvcolumn_value)
553 dlg.show_all()
554 ret = dlg.run()
555 if ret == gtk.RESPONSE_OK:
556 for row in self.liststore:
557 param = row[1]
558 reset = row[4] == "reset"
559 if reset:
560 #print "%s -> reset" % param.get_name()
561 param.param.reset_value()
562 else:
563 if param.modified:
564 #print "%s -> %s" % (param.get_name(), param.get_value())
565 param.maybe_save_value()
566 dlg.hide()
568 class jack_engine_params_configure_command(jack_params_configure_command):
569 def __init__(self):
570 jack_params_configure_command.__init__(self, ['engine'])
572 def get_description(self):
573 return 'JACK engine parameters'
575 class jack_driver_params_configure_command(jack_params_configure_command):
576 def __init__(self):
577 jack_params_configure_command.__init__(self, ['driver'])
579 def get_description(self):
580 return 'JACK driver parameters'
582 def get_window_title(self):
583 return 'JACK "%s" driver parameters' % jack.get_selected_driver()
585 class jack_internal_params_configure_command(jack_params_configure_command):
586 def __init__(self, name):
587 self.name = name
588 jack_params_configure_command.__init__(self, ['internals', name])
590 def get_description(self):
591 return 'JACK "%s" parameters' % self.name
593 check_ladish()
595 commands = [
596 jack_engine_params_configure_command(),
597 jack_driver_params_configure_command(),
600 for internal in jack.read_container(['internals']):
601 commands.append(jack_internal_params_configure_command(internal))
603 window = gtk.Window()
605 buttons_widget = gtk.VBox()
607 for command in commands:
608 button = gtk.Button(command.get_description())
609 button.connect('clicked', command.run)
610 buttons_widget.pack_start(button)
612 window.add(buttons_widget)
614 window.show_all()
615 window.connect('destroy', gtk.main_quit)
617 gtk.main()