Fix command execution on click.
[laditools.git] / laditools / gtk / ladimenu.py
blob566497b8ea591582764031812b551b94af9a16bf
1 #!/usr/bin/python
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/>.
21 import os
22 import sys
23 import subprocess
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:
47 self.menu_array = []
48 for element in menu_default:
49 self.menu_array.append(element)
51 self.proc_list = []
53 if jack_autostart:
54 self.jack_start ()
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):
80 try:
81 proxy = self.get_jack_controller()
82 except:
83 return False
84 return proxy and proxy.is_available()
86 def jack_start(self):
87 # This has nothing to do here... I suppose
88 # self.set_starting_status()
89 self.get_jack_controller().start()
91 def jack_stop(self):
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):
128 try:
129 proxy = self.get_a2j_controller()
130 except:
131 return False
132 if proxy.is_available():
133 return True
134 self.clear_a2j_controller()
135 return False
137 def a2j_is_started(self):
138 return self.get_a2j_controller().is_started()
140 def a2j_start(self):
141 self.get_a2j_controller().start()
143 def a2j_stop(self):
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):
159 try:
160 proxy = self.get_ladish_controller()
161 except:
162 return False
163 if proxy.is_available():
164 return True
165 self.clear_ladish_controller()
166 return False
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"), "")
173 if accept:
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):
186 dlg = Gtk.Dialog(
187 title,
188 None,
189 Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT)
190 dlg.add_buttons (Gtk.STOCK_CANCEL,
191 Gtk.ResponseType.REJECT,
192 Gtk.STOCK_OK,
193 Gtk.ResponseType.ACCEPT)
195 hbox = Gtk.HBox()
196 hbox.pack_start(Gtk.Label(label), True, True, 0)
197 entry = Gtk.Entry()
198 entry.set_text(text)
199 hbox.pack_start(entry, True, True, 0)
200 dlg.vbox.pack_start(hbox, True, True, 0)
201 dlg.show_all()
202 #entry.set_activates_default(True)
203 #dlg.set_default_response(Gtk.ResponseType.OK)
204 ret = dlg.run()
205 dlg.destroy()
206 if ret == Gtk.ResponseType.ACCEPT:
207 return True, entry.get_text()
208 else:
209 return False, text
211 def studio_rename(self):
212 accept, name = self.name_dialog(_("Rename studio"),
213 _("Studio name"),
214 self.get_ladish_controller().studio_name())
215 if accept:
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)
228 ret = dlg.run()
229 dlg.destroy()
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"))
240 dlg.run()
241 dlg.destroy()
243 def on_menu_command(self, widget, function):
244 try:
245 function()
246 except Exception, e:
247 error = Gtk.MessageDialog(None,
248 Gtk.DialogFlags.MODAL,
249 Gtk.MessageType.ERROR,
250 Gtk.ButtonsType.OK,
251 _("Error executing ") +
252 repr(function) +
253 _("Unexpected error\n\n") + repr(e))
254 error.run()
255 error.destroy()
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)
269 try:
270 jack = self.get_jack_configure()
271 # 'engine' item
272 item = Gtk.MenuItem(_("JACK engine"))
273 item.show()
274 menu.append(item)
275 item.connect("button-release-event", function, "engine")
276 # 'params' item
277 item = Gtk.MenuItem(_('JACK "%s" driver') % jack.get_selected_driver())
278 item.show()
279 menu.append(item)
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)
284 item.show()
285 menu.append(item)
286 item.connect("button-release-event", function, module)
287 except Exception, err:
288 print str(err)
289 if not menu.get_children():
290 item = Gtk.MenuItem(_("Empty config list"))
291 item.set_sensitive(False)
292 item.show()
293 menu.append(item)
295 def studio_list_fill(self, widget, function):
296 menu = widget.get_submenu()
297 self.menu_clear(menu)
298 try:
299 for studio in self.get_ladish_controller().studio_list():
300 item = Gtk.MenuItem(studio)
301 item.show()
302 menu.append(item)
303 item.connect("button-release-event", function, studio) # "activate" is not used because of focus bug in pygtk
304 except Exception, e:
305 self.menu_clear(menu)
306 item = Gtk.MenuItem(_("Error obtaining studio list"))
307 item.set_sensitive(False)
308 item.show()
309 menu.append(item)
310 if not menu.get_children():
311 item = Gtk.MenuItem(_("Empty studio list"))
312 item.set_sensitive(False)
313 item.show()
314 menu.append(item)
316 def create_menu(self):
317 menu_items = []
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))
331 continue
332 menu_label = items.keys()[0] + "..."
333 path = items.values()[0]
334 menu_items.append((Gtk.ImageMenuItem(menu_label), self.on_menu_launcher, path))
336 menu = Gtk.Menu()
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))
357 else:
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))
369 else:
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:
376 item = menu_tuple[0]
377 if len(menu_tuple) > 1:
378 callback = menu_tuple[1]
379 exec_path = menu_tuple[2]
380 menu.append(item)
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)
385 menu.show_all()
386 return menu
388 def menu_activate(self):
389 menu = self.create_menu()
390 menu.popup(parent_menu_shell=None,
391 parent_menu_item=None,
392 func=None,
393 data=None,
394 button=3,
395 activate_time=0)
396 menu.reposition()