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 @@
+
-
-
+
- 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
-
-
-
-
-
-
-
-
-
-
@@ -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
Extensions
True
@@ -1696,12 +2596,12 @@
gtk-quit
True
False
True
True
-
+
@@ -1732,24 +2632,24 @@
gtk-delete
True
False
True
True
+
-
@@ -1933,12 +2833,12 @@
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
+