Index: _package.epm
==================================================================
--- _package.epm
+++ _package.epm
@@ -29,11 +29,12 @@
 f 755 root root /usr/bin/streamtuner2				./st2.py
 f 644 root root /usr/share/applications/streamtuner2.desktop	./streamtuner2.desktop
 d 755 root root /usr/share/streamtuner2				-
 f 644 root root /usr/share/streamtuner2/streamtuner2.png	./streamtuner2.png
 f 644 root root /usr/share/pixmaps/streamtuner2.png		./logo.png
-f 644 root root /usr/share/streamtuner2/ui.xml			./ui.xml
+f 644 root root /usr/share/streamtuner2/gtk2.xml		./gtk2.xml
+f 644 root root /usr/share/streamtuner2/gtk3.xml		./gtk3.xml
 f 644 root root /usr/share/streamtuner2/pson.py			./pson.py
 #f 644 root root /usr/share/streamtuner2/processing.py		./processing.py
 f 644 root root /usr/share/streamtuner2/action.py		./action.py
 f 644 root root /usr/share/streamtuner2/config.py		./config.py
 f 644 root root /usr/share/streamtuner2/http.py			./http.py

Index: action.py
==================================================================
--- action.py
+++ action.py
@@ -22,19 +22,13 @@
 
 
 import re
 import os
 import http
-from config import conf
+from config import conf, __print__, dbg
 import platform
 
-
-#from channels import __print__
-def __print__(*args):
-    if conf.debug:
-        print(" ".join([str(a) for a in args]))
-
 
 main = None
 
 
 #-- media actions                           ---------------------------------------------

Index: channels/__init__.py
==================================================================
--- channels/__init__.py
+++ channels/__init__.py
@@ -4,6 +4,5 @@
 # type: R
 #
 
 
 from channels._generic import *
-from channels._generic import __print__

Index: channels/_generic.py
==================================================================
--- channels/_generic.py
+++ channels/_generic.py
@@ -20,19 +20,18 @@
 #
 
 
 import gtk
 from mygtk import mygtk
-from config import conf
+from config import conf, __print__, dbg
 import http
 import action
 import favicon
 import os.path
 import xml.sax.saxutils
 import re
 import copy
-
 
 
 # dict==object
 class struct(dict):
         def __init__(self, *xargs, **kwargs):
@@ -207,11 +206,11 @@
       
                 else:
                     # parse error
                     self.parent.status("category parsed empty.")
                     self.streams[category] = [{"title":"no contents found on directory server","bitrate":0,"max":0,"listeners":0,"playing":"error","favourite":0,"deleted":0}]
-                    print("oooops, parser returned nothing for category " + category)
+                    __print__(dbg.ERR, "Oooops, parser returned nothing for category " + category)
                     
             # assign to treeview model
             #self.streams[self.default] = []
             #if (self.liststore.has_key(category)):  # was already loded before
             #    self.gtk_list.set_model(self.liststore[category])
@@ -302,19 +301,17 @@
             self.load(self.current, force=0)
             
             
         # display .current category, once notebook/channel tab is first opened
         def first_show(self):
-            print("first_show ", self.module)
-            print 1
-            print self.shown
+            __print__(dbg.PROC, "first_show ", self.module, self.shown)
+
             if (self.shown != 55555):
-                print 2
             
                 # if category tree is empty, initialize it
                 if not self.categories:
-                    print 3
+                    __print__(dbg.PROC, "first_show: reload_categories");
                     #self.parent.thread(self.reload_categories)
                     print("reload categories");
                     self.reload_categories()
                     self.display_categories()
                     self.current = self.categories.keys()[0]
@@ -321,16 +318,16 @@
                     print self.current
                     self.load(self.current)
             
                 # load current category
                 else:
-                    print 4
+                    __print__(dbg.STAT, "first_show: load current category");
                     self.load(self.current)
                 
                 # put selection/cursor on last position
                 try:
-                    print 5
+                    __print__(dbg.STAT, "first_show: select last known category treelist position")
                     self.gtk_list.get_selection().select_path(self.shown)
                 except:
                     pass
                     
                 # this method will only be invoked once
@@ -529,19 +526,8 @@
                 #parent.channels[module] = None
                 #parent.channel_names.append(module)
                 """ -> already taken care of in main.load_plugins() """
 
 
-
-
-
-
-# wrapper for all print statements
-def __print__(*args):
-    if conf.debug:
-        print(" ".join([str(a) for a in args]))
-
-__debug_print__ = __print__
-
 
 
 

Index: channels/global_key.py
==================================================================
--- channels/global_key.py
+++ channels/global_key.py
@@ -11,11 +11,10 @@
 # it switches the currently playing radio station to another one in
 # bookmarks list.
 #
 # Valid key names are for example F9, <Ctrl>G, <Alt>R, <Super>N
 #
-return
 
 
 import keybinder
 from config import conf
 import action

Index: channels/internet_radio_org_uk.py
==================================================================
--- channels/internet_radio_org_uk.py
+++ channels/internet_radio_org_uk.py
@@ -12,11 +12,11 @@
 
 
 
 from channels import *
 import re
-from config import conf
+from config import conf, __print__, dbg
 import http
 from pq import pq
 
 
 

Index: channels/live365.py
==================================================================
--- channels/live365.py
+++ channels/live365.py
@@ -10,11 +10,11 @@
 # streamtuner2 modules
 from config import conf
 from mygtk import mygtk
 import http
 from channels import *
-from channels import __print__
+from config import __print__, dbg
 
 # python modules
 import re
 import xml.dom.minidom
 from xml.sax.saxutils import unescape as entity_decode, escape as xmlentities
@@ -116,14 +116,14 @@
             >DrawRatingStars\((\d+),\s+(\d+),.*?
                 """, re.X|re.I|re.S|re.M)
 #            src="(http://www.live365.com/.+?/stationlogo\w+.jpg)".+?
 
             # append entries to result list
-            __print__( html )
+            __print__( dbg.DATA, html )
             ls = []
             for row in rx.findall(html):
-                __print__( row )
+                __print__( dbg.DATA, row )
                 points = int(row[8])
                 count = int(row[9])
                 ls.append({
                     "launch_id": row[0],
                     "sofo": row[0],  # subscribe-or-fuck-off status flags

Index: channels/modarchive.py
==================================================================
--- channels/modarchive.py
+++ channels/modarchive.py
@@ -13,11 +13,11 @@
 
 import re
 import http
 from config import conf
 from channels import *
-from channels import __print__
+from config import __print__, dbg
 from xml.sax.saxutils import unescape
 
 
 
 
@@ -109,11 +109,11 @@
             .*?    >Rated</a>\s*(\d+)
         """, re.X|re.S)
         
         for uu in rx_mod.findall(html):
             (url, id, fmt, title, file, rating) = uu
-            __print__( uu )
+            __print__( dbg.DATA, uu )
             entries.append({
                 "genre": cat,
                 "url": url,
                 "id": id,
                 "format": self.mime_fmt(fmt) + "+zip",

Index: channels/punkcast.py
==================================================================
--- channels/punkcast.py
+++ channels/punkcast.py
@@ -11,11 +11,11 @@
 import re
 import http
 from config import conf
 import action
 from channels import *
-from channels import __print__
+from config import __print__, dbg
 
 
 
 
 
@@ -84,11 +84,11 @@
         rx_sound = re.compile("""(http://[^"<>]+[.](mp3|ogg|m3u|pls|ram))""")
         html = http.get(row["homepage"])
         
         # look up ANY audio url
         for uu in rx_sound.findall(html):
-            __print__( uu )
+            __print__( dbg.DATA, uu )
             (url, fmt) = uu
             action.action.play(url, self.mime_fmt(fmt), "url/direct")
             return
         
         # or just open webpage

Index: channels/shoutcast.py
==================================================================
--- channels/shoutcast.py
+++ channels/shoutcast.py
@@ -1,46 +1,32 @@
 #
 # api: streamtuner2
 # title: shoutcast
 # description: Channel/tab for Shoutcast.com directory
 # depends: pq, re, http
-# version: 1.2
+# version: 1.3
 # author: Mario
 # original: Jean-Yves Lefort
 #
 # Shoutcast is a server software for audio streaming. It automatically spools
-# station information on shoutcast.com, which this plugin can read out. But
-# since the website format is often changing, we now use PyQuery HTML parsing
-# in favour of regular expression (which still work, are faster, but not as
-# reliable).
-#
-# This was previously a built-in channel plugin. It just recently was converted
-# from a glade predefined GenericChannel into a ChannelPlugin.
-#
-#
-# NOTES
-#
-# Just found out what Tunapie uses:
-#    http://www.shoutcast.com/sbin/newxml.phtml?genre=Top500
-# It's a simpler list format, no need to parse HTML. However, it also lacks
-# homepage links. But maybe useful as alternate fallback...
-# Also:
-#   http://www.shoutcast.com/sbin/newtvlister.phtml?alltv=1
-#   http://www.shoutcast.com/sbin/newxml.phtml?search=
+# station information on shoutcast.com, which this plugin can read out.
+#
+# After its recent aquisition the layout got slimmed down considerably. So
+# there's not a lot of information to fetch left. And this plugin is now back
+# to defaulting to regex extraction instead of HTML parsing & DOM extraction.
 #
 #
 #
 
 
 import http
 import urllib
 import re
+from config import conf, __print__, dbg
 from pq import pq
-from config import conf
 #from channels import *    # works everywhere but in this plugin(???!)
 import channels
-__print__ = channels.__print__
 
 
 
 # SHOUTcast data module                                          ----------------------------------------
 class shoutcast(channels.ChannelPlugin):
@@ -75,17 +61,17 @@
         # extracts the category list from shoutcast.com,
         # sub-categories are queried per 'AJAX'
         def update_categories(self):
             html = http.get(self.base_url)
             self.categories = []
-            __print__( html )
+            __print__( dbg.DATA, html )
 
             # <h2>Radio Genres</h2>
 	    rx = re.compile(r'<li((?:\s+id="\d+"\s+class="files")?)><a href="\?action=sub&cat=([\w\s]+)#(\d+)">[\w\s]+</a>', re.S)
             sub = []
             for uu in rx.findall(html):
-                __print__(uu)
+                __print__( dbg.DATA, uu )
 		(main,name,id) = uu
                 name = urllib.unquote(name)
 
                 # main category
                 if main:
@@ -95,11 +81,11 @@
                     self.categories.append(name)
                 else:
                     sub.append(name)
 
             # it's done
-            __print__(self.categories)
+            __print__( dbg.PROC, self.categories )
             conf.save("cache/categories_shoutcast", self.categories)
             pass
 
 
 
@@ -109,11 +95,11 @@
 
         # downloads stream list from shoutcast for given category
         def update_streams(self, cat, search=""):
 
             if (not cat or cat == self.empty):
-                __print__("nocat")
+                __print__( dbg.ERR, "nocat" )
                 return []
             ucat = urllib.quote(cat)
 
 
             # loop
@@ -129,20 +115,26 @@
 
                   #/radiolist.cfm?action=sub&string=&cat=Oldies&_cf_containerId=radiolist&_cf_nodebug=true&_cf_nocache=true&_cf_rc=0
                   #/radiolist.cfm?start=19&action=sub&string=&cat=Oldies&amount=18&order=listeners
                   # page
                   url = "http://www.shoutcast.com/radiolist.cfm?action=sub&string=&cat="+ucat+"&order=listeners&amount="+str(count)
-                  __print__(url)
+                  __print__(dbg.HTTP, url)
                   referer = "http://www.shoutcast.com/?action=sub&cat="+ucat
                   params = {} # "strIndex":"0", "count":str(count), "ajax":"true", "mode":"listeners", "order":"desc" }
                   html = http.ajax(url, params, referer)   #,feedback=self.parent.status)
 
-                  __print__(html)
-                  __print__(re.compile("id=(\d+)").findall(html));
+                  __print__(dbg.DATA, html)
+                  #__print__(re.compile("id=(\d+)").findall(html));
+
+
+                  # With the new shallow <td> lists it doesn't make much sense to use
+                  # the pyquery DOM traversal. There aren't any sensible selectors to
+                  # extract values; it's just counting the tags.
+
 
-                  # regular expressions
-                  if 1:  #not conf.get("pyquery") or not pq:
+                  # regular expressions (default)
+                  if not conf.get("pyquery") or not pq:
 
                       # new html
                       """ 
                       <tr>
                          <td width="6%"><a href="#" onClick="window.open('player/?radname=Schlagerhoelle%20%2D%20das%20Paradies%20fr%20Schlager%20%20und%20Discofox&stationid=14687&coding=MP3','radplayer','height=232,width=776')"><img class="icon transition" src="/img/icon-play.png" alt="Play"></a></td>
@@ -165,30 +157,32 @@
                                \s+  <td [^>]+  >(\d+)</td>
                                \s+  <td [^>]+  >(\w+)</td>
                               """,
                               re.S|re.I|re.X
                           )
-                      __print__( rx_stream)
+
 
                       # extract entries
                       self.parent.status("parsing document...")
-                      __print__("loop-rx")
+                      __print__(dbg.PROC, "channels.shoutcast.update_streams: regex scraping mode")
+
                       for m in rx_stream.findall(html):
-                          __print__(m)
+                          #__print__(m)
                           (id, title, genre, listeners, bitrate, fmt) = m
                           entries += [{
                               "id": id,
                               "url": "http://yp.shoutcast.com/sbin/tunein-station.pls?id=" + id,
                               "title": self.entity_decode(title),
                               #"homepage": http.fix_url(homepage),
                               #"playing": self.entity_decode(playing),
                               "genre": genre,
                               "listeners": int(listeners),
-                              #"max": 0, #int(uu[6]),
+                              "max": 0, #int(uu[6]),
                               "bitrate": int(bitrate),
                               "format": self.mime_fmt(fmt),
                           }]
+
 
                   # PyQuery parsing
                   else:
                       # iterate over DOM
                       for div in (pq(e) for e in pq(html).find("tr")):
@@ -195,14 +189,13 @@
 
                           entries.append({
                                "title": div.find("a.transition").text(),
                                "url": div.find("a.transition").attr("href"),
                                "homepage": "",
-                               "playing": div.find("td:eq(2)").text(),
-                               "listeners": int(div.find("td:eq(4)").text()),
-                               "bitrate": int(div.find("td:eq(5)").text()),
-                               "format": self.mime_fmt(div.find("td:eq(6)").text()),
+                               "listeners": int(div.find("td:eq(3)").text()),
+                               "bitrate": int(div.find("td:eq(4)").text()),
+                               "format": self.mime_fmt(div.find("td:eq(5)").text()),
                                "max": 0,
                                "genre": cat,
                           })
 
 
@@ -212,13 +205,13 @@
                   
                   # more pages to load?
                   next = 99999
                      
             except Exception as e:
-               __print__(e)
+               __print__(dbg.ERR, e)
                return entries
             
             #fin
-            __print__(entries)
+            __print__(dbg.DATA, entries)
             return entries
 
 

Index: channels/xiph.py
==================================================================
--- channels/xiph.py
+++ channels/xiph.py
@@ -19,11 +19,11 @@
 # streamtuner2 modules
 from config import conf
 from mygtk import mygtk
 import http
 from channels import *
-from channels import __print__
+from config import __print__, dbg
 
 # python modules
 import re
 from xml.sax.saxutils import unescape as entity_decode, escape as xmlentities
 import xml.dom.minidom
@@ -88,11 +88,11 @@
             g = [ [v[1],v[0]] for v in g.items() ]
             g.sort()
             g.reverse()
             for row in g:
                 pass
-                __print__( '        "' + row[1] + '", #' + str(row[0]) )
+                __print__( dbg.DATA, '        "' + row[1] + '", #' + str(row[0]) )
 
 
         # xml dom node shortcut to text content
         def x(self, entry, name):
             e = entry.getElementsByTagName(name)

Index: config.py
==================================================================
--- config.py
+++ config.py
@@ -28,11 +28,10 @@
 
 
 #-- global configuration data               ---------------------------------------------
 class ConfigDict(dict):
 
-
         # start
         def __init__(self):
         
             # object==dict means conf.var is conf["var"]
             self.__dict__ = self  # let's pray this won't leak memory due to recursion issues
@@ -160,17 +159,17 @@
                     return # file not found
                 # decode
                 r = pson.load(f)
                 f.close()
                 return r
-            except (Exception), e:
+            except Exception as e:
                 print("PSON parsing error (in "+name+")", e)
             
 
         # recursive dict update
         def update(self, with_new_data):
-            for key,value in with_new_data.iteritems():
+            for key,value in with_new_data.items():
                 if type(value) == dict:
                     self[key].update(value)
                 else:
                     self[key] = value
             # descends into sub-dicts instead of wiping them with subkeys
@@ -187,6 +186,26 @@
 #-- actually fill global conf instance
 conf = ConfigDict()
 
 
 
+
+# wrapper for all print statements
+def __print__(*args):
+    if conf.debug:
+        print(" ".join([str(a) for a in args]))
+
+
+# error colorization
+dbg = type('obj', (object,), {
+    "ERR":  "[ERR]",  # red    ERROR
+    "INIT": "[INIT]", # red    INIT ERROR
+    "PROC": "[PROC]", # green  PROCESS
+    "CONF": "[CONF]", # brown  CONFIG DATA
+    "UI":   "[UI]",   # blue   USER INTERFACE BEHAVIOUR
+    "HTTP": "[HTTP]", # magenta HTTP REQUEST
+    "DATA": "[DATA]", # cyan   DATA
+    "INFO": "[INFO]", # gray   INFO
+    "STAT": "[STATE]", # gray  CONFIG STATE
+})
+
 

Index: http.py
==================================================================
--- http.py
+++ http.py
@@ -12,15 +12,18 @@
 #  The latter code is pretty much unreadable. But let's put the
 #  blame on urllib2, the most braindamaged code in the Python
 #  standard library.
 #
             
-
-import urllib2
-from urllib import urlencode
+try:
+    import urllib2
+    from urllib import urlencode
+except:
+    import urllib.request as urllib2
+    import urllib.parse.urlencode as urlencode
 import config
-from channels import __print__
+from config import __print__, dbg
 
 
 
 #-- url download                            ---------------------------------------------
 

Index: mygtk.py
==================================================================
--- mygtk.py
+++ mygtk.py
@@ -25,16 +25,15 @@
 
 
 
 
 # debug
-def __print__(*args):
-        print(" ".join([str(a) for a in args]))
+from config import __print__, dbg
 
 
 # gtk modules
-gtk = 3   # 0=gtk2, else gtk3
+gtk = 0   # 0=gtk2, else gtk3
 if gtk:
     from gi import pygtkcompat as pygtk
     pygtk.enable() 
     pygtk.enable_gtk(version='3.0')
     from gi.repository import Gtk as gtk

Index: pson.py
==================================================================
--- pson.py
+++ pson.py
@@ -19,10 +19,14 @@
 #
 
 
 #-- reading and writing json (for the config module)  ----------------------------------
 
+import sys
+if sys.version_info > (2, 9):
+    unicode = str
+    #dict.iteritems = dict.items
 
 # try to load the system module first
 try:
         from json import dump as json_dump, load as json_load
 except:
@@ -82,15 +86,15 @@
         elif type(obj) in (list, tuple, set):
                 obj = list(obj)
                 for i,v in enumerate(obj):
                         obj[i] = filter_data(v)
         elif type(obj) == dict:
-                for i,v in obj.iteritems():
+                for i,v in list(obj.items()):
                         i = filter_data(i)
                         obj[i] = filter_data(v)
         else:
                 print("invalid object in data, converting to string: ", type(obj), obj)
                 obj = str(obj)
         return obj
 
 
 

Index: st2.py
==================================================================
--- st2.py
+++ st2.py
@@ -97,14 +97,14 @@
 # gtk modules
 from mygtk import pygtk, gtk, gobject, ui_file, mygtk
 
 # custom modules
 from config import conf   # initializes itself, so all conf.vars are available right away
+from config import __print__, dbg
 import http
 import action  # needs workaround... (action.main=main)
 from channels import *
-from channels import __print__
 import favicon
 #from pq import pq
 
 
 
@@ -128,24 +128,24 @@
 
         # constructor
         def __init__(self):
 
             # gtkrc stylesheet
-            self.load_theme(), gui_startup(1/20)
+            self.load_theme(), gui_startup(1/20.0)
 
             # instantiate gtk/glade widgets in current object
             gtk.Builder.__init__(self)
-            gtk.Builder.add_from_file(self, conf.find_in_dirs([".", conf.share], ui_file)), gui_startup(2/20)
+            gtk.Builder.add_from_file(self, conf.find_in_dirs([".", conf.share], ui_file)), gui_startup(2/20.0)
             # manual gtk operations
             self.extensionsCTM.set_submenu(self.extensions)  # duplicates Station>Extension menu into stream context menu
 
             # initialize channels
             self.channels = {
               "bookmarks": bookmarks(parent=self),   # this the remaining built-in channel
               "shoutcast": None,#shoutcast(parent=self),
             }
-            gui_startup(3/20)
+            gui_startup(3/20.0)
             self.load_plugin_channels()   # append other channel modules / plugins
 
 
             # load application state (widget sizes, selections, etc.)
             try:
@@ -160,18 +160,18 @@
                         self.channels[id].shown = winlayout[id+"_list"].get("row:selected", 0)   # actually just used as boolean flag (for late loading of stream list), selection bar has been positioned before already
             except:
                 pass # fails for disabled/reordered plugin channels
 
             # display current open channel/notebook tab
-            gui_startup(17/20)
+            gui_startup(17/20.0)
             self.current_channel = self.current_channel_gtk()
             try: self.channel().first_show()
-            except: print("channel .first_show() initialization error")
+            except: __print__(dbg.INIT, "main.__init__: current_channel.first_show() initialization error")
 
       
             # bind gtk/glade event names to functions
-            gui_startup(19/20)
+            gui_startup(19/20.0)
             self.connect_signals(dict( {
                 "gtk_main_quit" : self.gtk_main_quit,                # close window
                 # treeviews / notebook
                 "on_stream_row_activated" : self.on_play_clicked,    # double click in a streams list
                 "on_category_clicked": self.on_category_clicked,     # new selection in category list
@@ -221,11 +221,11 @@
                 "streamedit_new": streamedit.new,
                 "streamedit_cancel": streamedit.cancel,
             }.items() + self.add_signals.items() ))
             
             # actually display main window
-            gui_startup(99/100)
+            gui_startup(99/100.0)
             self.win_streamtuner2.show()
             
             # WHY DON'T YOU WANT TO WORK?!
             #self.shoutcast.gtk_list.set_enable_search(True)
             #self.shoutcast.gtk_list.set_search_column(4)
@@ -281,15 +281,15 @@
             else: #if type(page_num) == int:
                 self.current_channel = self.channel_names[page_num]
             
             # if first selected, load current category
             try:
-                print("try: .first_show", self.channel().module);
-                print(self.channel().first_show)
-                print(self.channel().first_show())
+                __print__(dbg.PROC, "channel_switch: try .first_show", self.channel().module);
+                __print__(self.channel().first_show)
+                __print__(self.channel().first_show())
             except:
-                print("channel .first_show() initialization error")
+                __print__(dbg.INIT, "channel .first_show() initialization error")
 
 
         # convert ListStore iter to row number
         def rowno(self):
             (model, iter) = self.model_iter()
@@ -335,17 +335,17 @@
 
              
         # browse channel
         def on_homepage_channel_clicked(self, widget, event=2):
             if event == 2 or event.type == gtk.gdk._2BUTTON_PRESS:
-                __print__("dblclick")
+                __print__(dbg.UI, "dblclick")
                 action.browser(self.channel().homepage)            
 
 
         # reload stream list in current channel-category
         def on_reload_clicked(self, widget=None, reload=1):
-            __print__("reload", reload, self.current_channel, self.channels[self.current_channel], self.channel().current)
+            __print__(dbg.UI, "reload", reload, self.current_channel, self.channels[self.current_channel], self.channel().current)
             category = self.channel().current
             self.thread(
                 lambda: (  self.channel().load(category,reload), reload and self.bookmarks.heuristic_update(self.current_channel,category)  )
             )
 
@@ -365,11 +365,11 @@
 
         
         # click in category list
         def on_category_clicked(self, widget, event, *more):
             category = self.channel().currentcat()
-            __print__("on_category_clicked", category, self.current_channel)
+            __print__(dbg.UI, "on_category_clicked", category, self.current_channel)
             self.on_reload_clicked(None, reload=0)
             pass
 
 
         # add current selection to bookmark store
@@ -462,15 +462,15 @@
             order = [module.strip() for module in conf.channel_order.lower().replace(".","_").replace("-","_").split(",")]
             ls = [module for module in (order) if (module in ls)] + [module for module in (ls) if (module not in order)]
 
             # step through
             for module in ls:
-                gui_startup(2/10 + 7/10 * float(ls.index(module))/len(ls), "loading module "+module)
+                gui_startup(2/10.0 + 7/10.0 * float(ls.index(module))/len(ls), "loading module "+module)
                                 
                 # skip module if disabled
                 if conf.plugins.get(module, 1) == False:
-                    __print__("disabled plugin:", module)
+                    __print__(dbg.STAT, "disabled plugin:", module)
                     continue
                 
                 # load plugin
                 try:
                     plugin = __import__("channels."+module, None, None, [""])
@@ -487,11 +487,11 @@
                     # other plugin types
                     else:
                         self.features[module] = plugin_class(parent=self)
                     
                 except Exception as e:
-                    print("error initializing:", module, ", exception:")
+                    __print__(dbg.INIT, "load_plugin_channels: error initializing:", module, ", exception:")
                     import traceback
                     traceback.print_exc()
 
             # default plugins
             conf.add_plugin_defaults(self.channels["bookmarks"].config, "bookmarks")
@@ -514,11 +514,11 @@
 
         # apply gtkrc stylesheet
         def load_theme(self):
             if conf.get("theme"):
                 for dir in (conf.dir, conf.share, "/usr/share"):
-                    f = dir + "/themes/" + conf.theme + "/gtk-2"+".0/gtkrc"
+                    f = dir + "/themes/" + conf.theme + "/gtk-2.0/gtkrc"
                     if os.path.exists(f):
                         gtk.rc_parse(f)
                 pass
 
 
@@ -774,11 +774,11 @@
         def apply(self, config, prefix="config_", save=0):
             for key,val in config.iteritems():
                 # map non-alphanumeric chars from config{} to underscores in according gtk widget names
                 id = re.sub("[^\w]", "_", key)
                 w = main.get_widget(prefix + id)
-                __print__("config_save", save, prefix+id, w, val)
+                __print__(dbg.CONF, "config", ("save" if save else "load"), prefix+id, w, val)
                 # recurse into dictionaries, transform: conf.play.audio/mp3 => conf.play_audio_mp3
                 if (type(val) == dict):
                     self.apply(val, prefix + id + "_", save)
                 # load or set gtk.Entry text field
                 elif (w and save and type(w)==gtk.Entry):
@@ -797,13 +797,20 @@
            # self.theme.combo_box_new_text()
             # 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)
+            # prepare liststore
+            store = gtk.ListStore(gobject.TYPE_STRING)
+            self.theme.set_model(store)
+            cell = gtk.CellRendererText()
+            self.theme.pack_start(cell, True)
+            self.theme.add_attribute(cell, "text", 0)
             # add to combobox
             for num,themename in enumerate(themes):
-                 self.theme.append_text(themename)
+                 store.append([themename])
                  if conf.theme == themename:
                      self.theme.set_active(num)
             # erase this function, so it only ever gets called once
             self.combobox_theme = lambda: None
 
@@ -1006,11 +1013,11 @@
 
 
         # simplified gtk TreeStore display logic (just one category for the moment, always rebuilt)
         def load(self, category, force=False):
             #self.liststore[category] = \
-#            print(category, self.streams.keys())
+            __print__(dbg.UI, category, self.streams.keys())
             mygtk.columns(self.gtk_list, self.datamap, self.prepare(self.streams.get(category,[])))
 
 
         # select a category in treeview
         def add_category(self, cat):
@@ -1101,11 +1108,11 @@
 
 
 
 #-- startup progress bar
 progresswin, progressbar = 0, 0
-def gui_startup(p=0/100, msg="streamtuner2 is starting"):
+def gui_startup(p=0/100.0, msg="streamtuner2 is starting"):
 
     global progresswin,progressbar
     if not progresswin:
 
         # GtkWindow "progresswin"
@@ -1113,11 +1120,11 @@
         progresswin.set_property("title", "streamtuner2")
         progresswin.set_property("default_width", 300)
         progresswin.set_property("width_request", 300)
         progresswin.set_property("default_height", 30)
         progresswin.set_property("height_request", 30)
-        progresswin.set_property("window_position", "center")
+        #progresswin.set_property("window_position", "center")
         progresswin.set_property("decorated", False)
         progresswin.set_property("visible", True)
 
         # GtkProgressBar "progressbar"
         progressbar = gtk.ProgressBar()
@@ -1149,11 +1156,11 @@
     if len(sys.argv) < 2:
     
         
         # prepare for threading in Gtk+ callbacks
         gobject.threads_init()
-        gui_startup(1/100)
+        gui_startup(1/100.0)
         
         # prepare main window
         main = StreamTunerTwo()
         
         # module coupling
@@ -1166,11 +1173,11 @@
             config_dialog.open(None)
             del conf.firstrun
 
 
         # run
-        gui_startup(100/100)
+        gui_startup(100/100.0)
         gtk.main()
         
         
     # invoke command-line interface
     else: