2 # LADITools - Linux Audio Desktop Integration Tools
3 # Copyright (C) 2011-2012 Alessio Treglia <quadrispro@ubuntu.com>
4 # Copyright (C) 2007-2010:
5 # * Marc-Olivier Barre <marco@marcochapeau.org>
6 # * Nedko Arnaudov <nedko@arnaudov.name>
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 from gi
.repository
import Gtk
25 from gi
.repository
import GObject
26 from laditools
import _gettext_domain
27 from laditools
import LadiConfiguration
28 from laditools
import JackConfigProxy
29 from laditools
import JackController
30 from laditools
import A2jController
31 from laditools
import LadishProxy
33 # Default launcher menu :
34 menu_default
= [{"Logs": "ladilog"}]
36 class LadiManager(object):
37 def __init__(self
, menu_config_array
, jack_autostart
= False):
38 self
.proxy_jack_controller
= None
39 self
.proxy_jack_configure
= None
40 self
.proxy_a2j_controller
= None
41 self
.proxy_ladish_controller
= None
42 self
.diagnose_text
= ""
43 # Handle the configuration and grab custom menu items
44 self
.menu_array
= menu_config_array
45 # Add some defaults if we don't already have a menu
46 if self
.menu_array
== None:
48 for element
in menu_default
:
49 self
.menu_array
.append(element
)
56 def set_diagnose_text(self
, text
):
57 self
.diagnose_text
= text
59 def clear_diagnose_text(self
):
60 self
.diagnose_text
= ""
62 def get_jack_controller(self
):
63 if not self
.proxy_jack_controller
:
64 self
.proxy_jack_controller
= JackController()
65 return self
.proxy_jack_controller
67 def is_jack_controller_available(self
):
68 return self
.proxy_jack_controller
!= None
70 def get_jack_configure(self
):
71 if not self
.proxy_jack_configure
:
72 self
.proxy_jack_configure
= JackConfigProxy()
73 return self
.proxy_jack_configure
75 def clear_jack_proxies(self
):
76 self
.proxy_jack_controller
= None
77 self
.proxy_jack_configure
= None
79 def jack_is_available(self
):
81 proxy
= self
.get_jack_controller()
84 return proxy
and proxy
.is_available()
87 # This has nothing to do here... I suppose
88 # self.set_starting_status()
89 self
.get_jack_controller().start()
92 self
.get_jack_controller().stop()
94 def jack_reactivate(self
):
95 self
.get_jack_controller().kill()
96 self
.clear_jack_proxies()
98 def jack_reset_xruns(self
):
99 self
.get_jack_controller().reset_xruns()
101 def jack_is_started(self
):
102 return self
.get_jack_controller().is_started()
104 def jack_is_realtime(self
):
105 return self
.get_jack_controller().is_realtime()
107 def jack_get_load(self
):
108 return self
.get_jack_controller().get_load()
110 def jack_get_xruns(self
):
111 return self
.get_jack_controller().get_xruns()
113 def jack_get_sample_rate(self
):
114 return self
.get_jack_controller().get_sample_rate()
116 def jack_get_latency(self
):
117 return self
.get_jack_controller().get_latency()
119 def get_a2j_controller(self
):
120 if not self
.proxy_a2j_controller
:
121 self
.proxy_a2j_controller
= A2jController()
122 return self
.proxy_a2j_controller
124 def clear_a2j_controller(self
):
125 self
.proxy_a2j_controller
= None
127 def a2j_is_available(self
):
129 proxy
= self
.get_a2j_controller()
132 if proxy
.is_available():
134 self
.clear_a2j_controller()
137 def a2j_is_started(self
):
138 return self
.get_a2j_controller().is_started()
141 self
.get_a2j_controller().start()
144 self
.get_a2j_controller().stop()
146 def a2j_reactivate(self
):
147 self
.get_a2j_controller().kill()
148 self
.clear_a2j_controller()
150 def get_ladish_controller(self
):
151 if not self
.proxy_ladish_controller
:
152 self
.proxy_ladish_controller
= LadishProxy()
153 return self
.proxy_ladish_controller
155 def clear_ladish_controller(self
):
156 self
.proxy_ladish_controller
= None
158 def ladish_is_available(self
):
160 proxy
= self
.get_ladish_controller()
163 if proxy
.is_available():
165 self
.clear_ladish_controller()
168 def studio_is_loaded(self
):
169 return self
.get_ladish_controller().studio_is_loaded()
171 def studio_new(self
):
172 accept
, name
= self
.name_dialog(_("New studio"), _("Studio name"), "")
174 self
.get_ladish_controller().studio_new(name
)
176 def studio_load(self
, item
, event
, studio
):
177 self
.get_ladish_controller().studio_load(studio
)
179 def studio_start(self
):
180 self
.get_ladish_controller().studio_start()
182 def studio_stop(self
):
183 self
.get_ladish_controller().studio_stop()
185 def name_dialog(self
, title
, label
, text
):
189 Gtk
.DialogFlags
.MODAL | Gtk
.DialogFlags
.DESTROY_WITH_PARENT
)
190 dlg
.add_buttons (Gtk
.STOCK_CANCEL
,
191 Gtk
.ResponseType
.REJECT
,
193 Gtk
.ResponseType
.ACCEPT
)
196 hbox
.pack_start(Gtk
.Label(label
), True, True, 0)
199 hbox
.pack_start(entry
, True, True, 0)
200 dlg
.vbox
.pack_start(hbox
, True, True, 0)
202 #entry.set_activates_default(True)
203 #dlg.set_default_response(Gtk.ResponseType.OK)
206 if ret
== Gtk
.ResponseType
.ACCEPT
:
207 return True, entry
.get_text()
211 def studio_rename(self
):
212 accept
, name
= self
.name_dialog(_("Rename studio"),
214 self
.get_ladish_controller().studio_name())
216 self
.get_ladish_controller().studio_rename(name
)
218 def studio_save(self
):
219 self
.get_ladish_controller().studio_save()
221 def studio_unload(self
):
222 self
.get_ladish_controller().studio_unload()
224 def studio_delete(self
, item
, event
, studio
):
225 dlg
= Gtk
.MessageDialog(None, Gtk
.DialogFlags
.MODAL
, Gtk
.MessageType
.ERROR
, Gtk
.ButtonsType
.YES_NO
, "")
226 dlg
.set_markup(_("<b><big>Confirm studio delete</big></b>"))
227 dlg
.format_secondary_text(_("Studio \"%s\" will be deleted. Are you sure?") % studio
)
230 if ret
== Gtk
.ResponseType
.YES
:
231 self
.get_ladish_controller().studio_delete(studio
)
233 def ladish_reactivate(self
):
234 self
.get_ladish_controller().kill()
235 self
.clear_ladish_controller()
237 def on_menu_show_diagnose(self
, widget
, data
=None):
238 dlg
= Gtk
.MessageDialog(None, Gtk
.DialogFlags
.MODAL
, Gtk
.MessageType
.ERROR
, Gtk
.ButtonsType
.CLOSE
, self
.diagnose_text
)
239 dlg
.set_title(_("Cannot communicate JACK D-Bus"))
243 def on_menu_command(self
, widget
, function
):
247 error
= Gtk
.MessageDialog(None,
248 Gtk
.DialogFlags
.MODAL
,
249 Gtk
.MessageType
.ERROR
,
251 _("Error executing ") +
253 _("Unexpected error\n\n") + repr(e
))
257 def on_menu_launcher(self
, widget
, exec_path
):
258 self
.proc_list
.append(subprocess
.Popen([exec_path
]))
260 def menu_clear(self
, menu
):
261 menu
.foreach(lambda item
,none
: menu
.remove(item
), None)
263 def studio_configure(self
, item
, event
, module
):
264 self
.proc_list
.append(subprocess
.Popen(["ladiconf", "-m", module
]))
266 def configure_list_fill(self
, widget
, function
):
267 menu
= widget
.get_submenu()
268 self
.menu_clear(menu
)
270 jack
= self
.get_jack_configure()
272 item
= Gtk
.MenuItem(_("JACK engine"))
275 item
.connect("button-release-event", function
, "engine")
277 item
= Gtk
.MenuItem(_('JACK "%s" driver') % jack
.get_selected_driver())
280 item
.connect("button-release-event", function
, "params")
281 for internal
in jack
.read_container(['internals']):
282 module
= str(internal
)
283 item
= Gtk
.MenuItem(_('JACK "%s"') % module
)
286 item
.connect("button-release-event", function
, module
)
287 except Exception, err
:
289 if not menu
.get_children():
290 item
= Gtk
.MenuItem(_("Empty config list"))
291 item
.set_sensitive(False)
295 def studio_list_fill(self
, widget
, function
):
296 menu
= widget
.get_submenu()
297 self
.menu_clear(menu
)
299 for studio
in self
.get_ladish_controller().studio_list():
300 item
= Gtk
.MenuItem(studio
)
303 item
.connect("button-release-event", function
, studio
) # "activate" is not used because of focus bug in pygtk
305 self
.menu_clear(menu
)
306 item
= Gtk
.MenuItem(_("Error obtaining studio list"))
307 item
.set_sensitive(False)
310 if not menu
.get_children():
311 item
= Gtk
.MenuItem(_("Empty studio list"))
312 item
.set_sensitive(False)
316 def create_menu(self
):
319 if self
.diagnose_text
:
320 menu_items
.append((Gtk
.ImageMenuItem(_("Diagnose")), self
.on_menu_show_diagnose
, None))
321 menu_items
.append((Gtk
.SeparatorMenuItem
.new(), None, None))
323 if self
.ladish_is_available():
324 menu_items
.append((Gtk
.ImageMenuItem(_("Start gladish")), self
.on_menu_launcher
, "gladish"))
326 # Add the laucher entries at the beginning of the menu
327 for items
in self
.menu_array
:
328 # Replace "Configure" static item with the new sub-menu
329 if items
.keys()[0] == 'Configure':
330 menu_items
.append((Gtk
.ImageMenuItem("Configure ..."), self
.configure_list_fill
, self
.studio_configure
))
332 menu_label
= items
.keys()[0] + "..."
333 path
= items
.values()[0]
334 menu_items
.append((Gtk
.ImageMenuItem(menu_label
), self
.on_menu_launcher
, path
))
337 menu_items
.append((Gtk
.SeparatorMenuItem
.new(),))
338 if self
.ladish_is_available():
339 menu_items
.append((Gtk
.ImageMenuItem(_("New studio...")), self
.on_menu_command
, self
.studio_new
))
340 menu_items
.append((Gtk
.ImageMenuItem(_("Load studio")), self
.studio_list_fill
, self
.studio_load
))
341 if self
.studio_is_loaded():
342 menu_items
.append((Gtk
.SeparatorMenuItem
.new(), None, None))
343 menu_items
.append((Gtk
.ImageMenuItem(_("Start studio")), self
.on_menu_command
, self
.studio_start
))
344 menu_items
.append((Gtk
.ImageMenuItem(_("Stop studio")), self
.on_menu_command
, self
.studio_stop
))
345 menu_items
.append((Gtk
.SeparatorMenuItem
.new(), None, None))
346 if self
.studio_is_loaded():
347 menu_items
.append((Gtk
.ImageMenuItem(_("Rename studio...")), self
.on_menu_command
, self
.studio_rename
))
348 menu_items
.append((Gtk
.ImageMenuItem(_("Save studio")), self
.on_menu_command
, self
.studio_save
))
349 menu_items
.append((Gtk
.ImageMenuItem(_("Unload studio")), self
.on_menu_command
, self
.studio_unload
))
350 menu_items
.append((Gtk
.ImageMenuItem(_("Delete studio")), self
.studio_list_fill
, self
.studio_delete
))
351 menu_items
.append((Gtk
.ImageMenuItem(_("Reactivate ladishd")), self
.on_menu_command
, self
.ladish_reactivate
))
352 menu_items
.append((Gtk
.SeparatorMenuItem
.new(), None, None))
353 if self
.jack_is_available():
354 if not self
.jack_is_started():
355 if not self
.ladish_is_available():
356 menu_items
.append((Gtk
.ImageMenuItem(_("Start JACK server")), self
.on_menu_command
, self
.jack_start
))
358 menu_items
.append((Gtk
.ImageMenuItem(_("Reset Xruns")), self
.on_menu_command
, self
.jack_reset_xruns
))
359 if not self
.ladish_is_available():
360 menu_items
.append((Gtk
.ImageMenuItem(_("Stop JACK server")), self
.on_menu_command
, self
.jack_stop
))
361 menu_items
.append((Gtk
.ImageMenuItem(_("Reactivate JACK")), self
.on_menu_command
, self
.jack_reactivate
))
362 menu_items
.append((Gtk
.SeparatorMenuItem
.new(), None, None))
363 if self
.a2j_is_available():
364 # when a2jmidid is used in used with ladish, a2j script should be used
365 # for managing bridge "active" lifetime
366 if not self
.ladish_is_available():
367 if not self
.a2j_is_started():
368 menu_items
.append((Gtk
.ImageMenuItem(_("Start A2J bridge")), self
.on_menu_command
, self
.a2j_start
))
370 menu_items
.append((Gtk
.ImageMenuItem(_("Stop A2J bridge")), self
.on_menu_command
, self
.a2j_stop
))
371 menu_items
.append((Gtk
.ImageMenuItem(_("Reactivate A2J")), self
.on_menu_command
, self
.a2j_reactivate
))
372 menu_items
.append((Gtk
.SeparatorMenuItem
.new(), None, None))
373 menu_items
.append((Gtk
.ImageMenuItem(_("Quit")), self
.on_menu_command
, Gtk
.main_quit
))
375 for menu_tuple
in menu_items
:
377 if len(menu_tuple
) > 1:
378 callback
= menu_tuple
[1]
379 exec_path
= menu_tuple
[2]
381 if type(item
) is not Gtk
.SeparatorMenuItem
:
382 if callback
in (self
.studio_list_fill
, self
.configure_list_fill
):
383 item
.set_submenu(Gtk
.Menu())
384 item
.connect("activate", callback
, exec_path
)
388 def menu_activate(self
):
389 menu
= self
.create_menu()
390 menu
.popup(parent_menu_shell
=None,
391 parent_menu_item
=None,