ADDED channels/configwin.py Index: channels/configwin.py ================================================================== --- channels/configwin.py +++ channels/configwin.py @@ -0,0 +1,195 @@ +# api: streamtuner2 +# title: Config dialog +# description: Allows to configure players, options, and plugins +# version: 2.5 +# type: feature +# category: ui +# config: - +# priority: core +# +# Configuration dialog for audio applications, +# general settings, and plugin activaiton and +# their options. + + +from uikit import * +import channels +from config import * +import re + + +# Settings window +# +# Interacts with main.* window (gtkBuilder widgets) +# and conf.* dictionary. +# +class configwin (AuxiliaryWindow): + + + # Display win_config, pre-fill text fields from global conf. object + def open(self, widget): + if self.first_open: + self.add_plugins() + self.combobox_theme() + self.first_open = 0 + self.win_config.resize(565, 625) + self.load_config(conf.__dict__, "config_") + self.load_config(conf.plugins, "config_plugins_") + [callback() for callback in self.hooks["config_load"]] + self.win_config.show_all() + first_open = 1 + + # Hide window + def hide(self, *args): + self.win_config.hide() + return True + + + # Load values from conf. store into gtk widgets + def load_config(self, config, prefix="config_"): + for key,val in config.items(): + w = self.main.get_widget(prefix + key) + if w: + # input field + if type(w) is gtk.Entry: + w.set_text(str(val)) + # checkmark + elif type(w) is gtk.CheckButton: + w.set_active(bool(val)) + # dropdown + elif type(w) is ComboBoxText: + w.set_default(val) + # list + elif type(w) is gtk.ListStore: + w.clear() + for k,v in val.items(): + w.append([k, v, True, self.app_bin_check(v)]) + w.append(["", "", True, gtk.STOCK_NEW]) + __print__(dbg.CONF, "config load", prefix+key, val, type(w)) + + # Store gtk widget valus back into conf. dict + def save_config(self, config, prefix="config_", save=0): + for key,val in config.items(): + w = self.main.get_widget(prefix + key) + if w: + # text + if type(w) is gtk.Entry: + config[key] = w.get_text() + # pre-defined text + elif type(w) is ComboBoxText: + config[key] = w.get_active_text() + # boolean + elif type(w) is gtk.CheckButton: + config[key] = w.get_active() + # dict + elif type(w) is gtk.ListStore: + config[key] = {} + for row in w: + if row[0] and row[1]: + config[key][row[0]] = row[1] + __print__(dbg.CONF, "config save", prefix+key, val) + + + # Generic Gtk callback to update ListStore when entries get edited + def list_edit(self, liststore, path, column, new_text): + liststore[path][column] = new_text + liststore[path][3] = self.app_bin_check(new_text) + # The signal_connect() dict actually prepares individual lambda functions + # to bind the correct ListStore and column id. + + # return OK or CANCEL depending on availability of app + def app_bin_check(self, v): + m = re.search("(?![$(`])\S+", v) + if m and m.group(0) and find_executable(m.group(0)): + return gtk.STOCK_MEDIA_PLAY + else: + return gtk.STOCK_CANCEL + + + # list of Gtk themes in dropdown + def combobox_theme(self): + # find themes + themedirs = (conf.share+"/themes", conf.dir+"/themes", "/usr/share/themes") + themes = ["no theme"] + [[themes.append(e) for e in os.listdir(dir)] for dir in themedirs if os.path.exists(dir)] + __print__(dbg.STAT, themes) + # add dropdown + self.widgets["theme"] = ComboBoxText(themes) + self.theme_cb_placeholder.pack_start(self.theme) + self.theme_cb_placeholder.pack_end(uikit.label("")) + + + # retrieve currently selected value + def apply_theme(self): + conf.theme = self.theme.get_active_text() + uikit.load_theme(conf.theme) + + + # iterate over channel and feature plugins + def add_plugins(self): + ls = {} + for name in module_list(): + if name in self.channels: + ls[name] = self.channels[name].meta + elif name in self.features: + ls[name] = self.features[name].meta + else: + ls[name] = plugin_meta(conf.share+"/channels/"+name+".py") + for name,meta in sorted(ls.items(), key=lambda e: e[1]["type"]+e[1]["title"].lower(), reverse=False): + self.add_plg(name, meta) + #self.plugin_options.pack_start(uikit.label("\nFeature plugins add categories, submenu entries, or other extensions.\n", 500, 1)) + + # add configuration setting definitions from plugins + plugin_text = "%s (%s/%s) %s\n%s" + def add_plg(self, name, meta): + # add plugin load entry + cb = gtk.CheckButton(name) + cb.set_sensitive(not meta.get("priority") in ("core", "required", "builtin")) + cb.get_children()[0].set_markup(self.plugin_text % (meta.get("title", name), meta.get("type", "plugin"), meta.get("category", "addon"), meta.get("version", "./."), meta.get("description", "no description"))) + cb.set_tooltip_text(re.sub("(?<=\S) *\n(?! *\n)", " ",meta.get("doc", "")).strip()) + self.add_( "config_plugins_"+name, cb, color=meta.get("color"), image=meta.get("png")) + + # default values are already in conf[] dict (now done in conf.add_plugin_defaults) + for opt in meta["config"]: + color = opt.get("color", None) + # display checkbox + if opt["type"] == "boolean": + cb = gtk.CheckButton(opt["description"]) + self.add_( "config_"+opt["name"], cb, color=color ) + # drop down list + elif opt["type"] == "select": + cb = ComboBoxText(ComboBoxText.parse_options(opt["select"])) # custom uikit widget + self.add_( "config_"+opt["name"], cb, opt["description"], color ) + # text entry + else: + self.add_( "config_"+opt["name"], gtk.Entry(), opt["description"], color ) + + # spacer + self.add_( "filler_pl_"+name, gtk.HSeparator() ) + + + # Put config widgets into config dialog notebook, wrap with label, or background, retain widget id/name + def add_(self, id=None, w=None, label=None, color=None, image=None): + if id: + self.widgets[id] = w + if label: + if type(w) is gtk.Entry: + w.set_width_chars(11) + w = uikit.hbox(w, uikit.label(label)) + if image: + pix = gtk.image_new_from_pixbuf(uikit.pixbuf(image)) + if pix: + w = uikit.hbox(w, pix, exr=False) + if color: + w = uikit.bg(w, color) + self.plugin_options.pack_start(w) + + # save config + def save(self, widget): + self.save_config(conf.__dict__, "config_") + self.save_config(conf.plugins, "config_plugins_") + [callback() for callback in self.hooks["config_save"]] + self.apply_theme() + conf.save(nice=1) + self.hide() + Index: config.py ================================================================== --- config.py +++ config.py @@ -28,13 +28,21 @@ import re import zipfile import inspect import pkgutil +# find_executable() is only needed by channels/configwin +try: + from distutils.spawn import find_executable +except: + def find_executable(bin): + exists = [os.path.exists(dir+"/"+bin) for dir in os.environ.get("PATH").split(":")+["/"]] + return exists[0] if len(exists) else None + # export symbols -__all__ = ["conf", "__print__", "dbg", "plugin_meta", "module_list", "get_data"] +__all__ = ["conf", "__print__", "dbg", "plugin_meta", "module_list", "get_data", "find_executable"] #-- create a stub instance of config object conf = object() Index: gtk3.xml ================================================================== --- gtk3.xml +++ gtk3.xml @@ -1,27 +1,31 @@ + - - + - + + + - + + + 565 False @@ -90,10 +94,13 @@ False True 0 8 both + + + 10 125 Format @@ -109,13 +116,19 @@ - 10 + 5 300 Application + + + + 3 + + @@ -175,10 +188,13 @@ False True 0 8 both + + + 10 125 Format @@ -197,10 +213,16 @@ 10 300 Application + + + + 3 + + @@ -456,12 +478,10 @@ 5 500 out False False - True - True False False 0 @@ -609,12 +629,10 @@ True gtk-save-as False False - True - True False False 1 @@ -635,10 +653,11 @@ Keep and reuse temporary .m3u files for played stations. True True False + 0.5 True True True @@ -678,12 +697,10 @@ 20 gtk-home False False - True - True False True 1 @@ -705,10 +722,11 @@ Enable _debug messages (on the console). True True False True + 0.5 True True True @@ -794,12 +812,10 @@ True True False False - True - True True True 1 @@ -1069,30 +1085,230 @@ gtk-add 325 False + 0.94999999999999996 5 station search center-on-parent True dialog center - 0.94999999999999996 - + + True False + 2 - + + True + False + end + + + False + False + end + 0 + + + + + True False + 20 + + + True + False + <b><big>search</big></b> + True + + + True + True + 0 + + + + + + + + True + False + + + True + False + 10 + for + + + True + True + 0 + + + + + True + True + True + True + A single word to search for in all stations. + + True + False + False + + + True + True + 1 + + + + + True + False + + + True + True + 2 + + + + + True + True + 2 + + + + + True + False + + + True + False + 10 + 10 + in + + + True + True + 0 + + + + + all channels + True + True + False + 0.5 + True + True + search_dialog_current + + + True + True + 1 + + + + + just current + True + True + False + 0.5 + True + True + search_dialog_all + + + True + True + 2 + + + + + True + True + 3 + + + + + + + + True + False + 20 + + + Cache _find + True + False + False + Start searching for above search term in the currently loaded station lists. Doesn't find *new* information, just looks through the known data. + image1 + half + True + + + + + True + True + 0 + + + + + Server _search + True + False + True + True + True + True + True + Instead of doing a cache search, go through the search functions on the directory service homepages. (UNIMPLEMENTED) + image2 + True + + + + True + True + 1 + + + + + True + True + 5 + + True True - 0 + 1 @@ -1101,41 +1317,35 @@ False True False - play + Play True True False - record + Record True True False - bookmark + Bookmark True - - True - False - - - - + True False Extensions True @@ -1145,44 +1355,32 @@ - - True - False - - - True False - save + Save True True False - edit + Edit / Details True - - True - False - - - True False - station homepage + Station homepage True @@ -1191,24 +1389,747 @@ 5 normal - + + True False + 2 - + + True + False + end + + + cancel + True + True + True + + + + False + False + 0 + + + + + ok + True + True + True + + + + False + False + 1 + + + + + False + True + end + 0 + + + + + True False + 3 + 3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + True + True + + Fri,Sat 20:00-21:00 + False + False + + + 1 + 2 + 1 + 2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + True True - 0 + 1 + + timer_cancel + timer_ok + False @@ -1253,16 +2174,16 @@ False + 0.94999999999999996 5 inspect/edit stream data center-on-parent True True - 0.94999999999999996 True False @@ -1292,12 +2213,10 @@ True True False False - True - True 1 2 1 @@ -1309,12 +2228,10 @@ True True False False - True - True 1 2 2 @@ -1326,12 +2243,10 @@ True True False False - True - True 1 2 3 @@ -1343,12 +2258,10 @@ True True False False - True - True 1 2 4 @@ -1360,12 +2273,10 @@ True True False False - True - True 1 2 5 @@ -1377,12 +2288,10 @@ True True False False - True - True 1 2 6 @@ -1538,12 +2447,10 @@ True True False False - True - True 1 2 8 @@ -1579,12 +2486,10 @@ True True False False - True - True 1 2 7 @@ -1601,15 +2506,10 @@ streamtuner2 980 775 /usr/share/pixmaps/streamtuner2.png applications-multimedia - - - streamtuner2 - - True False @@ -1639,42 +2539,42 @@ True False bookmark True + - gtk-save-as True False True True + - gtk-edit True False True True + - - + True False Extensions True @@ -1696,12 +2596,12 @@ gtk-quit True False True True - + @@ -1732,24 +2632,24 @@ gtk-delete True False True True + - gtk-find True False True True - + True @@ -1900,12 +2800,12 @@ gtk-properties True False True True - + @@ -1933,12 +2833,12 @@ True False Reload True - + True @@ -2127,12 +3027,10 @@ 10 0.10000000149011612 gtk-find False False - True - True False @@ -2160,11 +3058,10 @@ True False GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_MOTION_MASK | GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK | GDK_BUTTON3_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_FOCUS_CHANGE_MASK | GDK_STRUCTURE_MASK | GDK_PROPERTY_CHANGE_MASK | GDK_VISIBILITY_NOTIFY_MASK | GDK_PROXIMITY_IN_MASK | GDK_PROXIMITY_OUT_MASK | GDK_SUBSTRUCTURE_MASK | GDK_SCROLL_MASK 15 gtk-media-play - 100 False True @@ -2180,11 +3077,10 @@ True True - left True True @@ -2200,10 +3096,13 @@ 75 True True True + + + False @@ -2219,10 +3118,13 @@ 200 True True + + + True @@ -2250,21 +3152,20 @@ True False - Bookmarks + bookmarks True True 1 - bookmarks False @@ -2323,8 +3224,13 @@ True 2 + + + + streamtuner2 +