import gtk
import gtk.glade
import gobject
import threading
import time
import ticker
import sys
import os
import cgi
import ConfigParser
import traceback
import imp

try:
    import hotshot, hotshot.stats
except ImportError:
    print "Problem importing hotshot module. Don't worry"


class Dir:
    def __init__(self, base):
        self.plugin = base+"/plugins/"
        self.glade = base+"/glade/"
        self.base = base
#        sys.path.append(self.plugin)

ALL="<b>All</b>"
DATABASE="Database"
PLAYLIST="Now Playing"
HARDDRIVE="Hard Disk"
LIBRARY="Library"
TRUE=1
FALSE=-1
EQUAL=0
CONFIG_FILE=os.path.expanduser("~/.pympd/pympd.conf")
TRACEBACK=True

config = {}
keybindings = {}
action_defs = {}
actions = {
    'Browser Window': 'toggle_view_browser',
    'Clear Filters' : 'filter_clear',
    'Compact'       : 'compact_event',
    'Delete Song'   : 'playlist_remove',
    'Enqueue'       : 'browser_add',
    'Focus Filter'  : 'focus_filter',
    'Jump to Song'  : 'playlist_jump_to_song',
    'Next'          : 'next_event',
    'Play'          : 'play_event',
    'Plugins Window': 'toggle_view_plugins',
    'Preferences'   : 'prefs_activate', 
    'Previous'      : 'prev_event',
    'Repeat'        : 'repeat_clicked',
    'Shuffle'       : 'shuffle_clicked',
    'Song Info'     : 'playlist_info',
    'Toggle Menubar': 'toggle_view_menubar',
    'Toggle Sidebar': 'toggle_view_sidebar',
}
for action in actions:
    action_defs[actions[action]] = action

def profile(func, args=[]):
    profiler = hotshot.Profile('hotshot.prof')
    profiler.start()
    ret = apply(func, args)
    profiler.stop()
    stats = hotshot.stats.load("hotshot.prof")
    stats.strip_dirs()
    stats.sort_stats('time', 'calls', 'cumulative')
    stats.print_stats(20)
    return ret

def loadPsyco():
    try:
        import psyco
        psyco.full()
        debug("psyco code optimizer loaded", False)
    except ImportError:
        print "This program supports \"psyco\", an inline optimizer for python code, you can get it at http://sourceforge.net/projects/psyco"

def set(attr, value):
    config[attr.lower()] = value

def get(attr):
    return config[attr.lower()]

def get_bool(attr):
    value = config[attr.lower()]
    if str(value) == str(FALSE):
        return False
    else:
        return True

def set_bool(attr, value):
    if value:
        set(attr, TRUE)
    else:
        set(attr, FALSE)

def debug(message, trace=True):
    if get_bool('debug'):
        if not TRACEBACK:
            print message
        else:
            type, val, tb = sys.exc_info()
            if tb is not None and trace:
                print traceback.format_exc(),
            else:
                print message

def replaceAmp(string):
    return string.replace('&amp;', '&')

def insertAmp(string):
    return string.replace('&', '&amp;')

def iAmp(string):
    return string.replace('&', '&amp;')

def secToString(secs):
    try:
        secs = int(secs)
        minutes = secs / 60
        seconds = secs % 60
        if seconds < 10:
#            seconds = "0%(seconds)s" % locals()
            return "%(minutes)s:0%(seconds)s"%locals()

        return "%(minutes)s:%(seconds)s"%locals()
    except:
        return '0:00'

def fillSong(song):
    if not song.has_key('artist'):
        song['artist'] = ''
    if not song.has_key('title'):
        song['title'] = ''
    if not song.has_key('album'):
        song['album'] = ''
    if not song.has_key('track'):
        song['track'] = ''

def sepAtColon(string):
    split_index = string.find(':')
    ret = []
    ret.append(string[:split_index].strip())
    ret.append(string[split_index+1:].strip())
    return ret

def makeBold(string):
    return "<b>" + string + "</b>"

def stripBold(string):
    if string.find('<b>') > -1:
        return string[string.find('<b>')+3:string.find('</b>')]
    return string

def stripExt(string):
    if string.rfind('.') > -1:
        return string[:string.rfind('.')], string[string.rfind('.')+1:]
    return string, ''

def rowFromIter(store, iter, columns):
    toappend = []
    for i in xrange(columns):
        toappend.append(store.get_value(iter, i))
    return toappend

def threadsafe(func, args=None):
    gtk.gdk.threads_enter()
    if args == None:
        ret = func()
    else:
        ret = apply(func, args)
    gtk.gdk.threads_leave()
    return ret

def readDir(iter, parent, treestore, mpd):
    try:
        contents = mpd.lsInfo(parent)
        append = treestore.append
        for item in contents:
            if item.type == 'file':
                if item.title == '':
                    item.title = os.path.splitext(os.path.basename(item.file))[0]
                append(iter, ("audio-x-generic", item.title, item.file))
            elif item.type == 'directory':
                dirs = item.directory.split('/')
                path = dirs[len(dirs)-1]
                child_iter = append(iter, ("folder", path, item.directory))
                treestore.append(child_iter)
            else:
                debug("Unknown Type: %s" % item.type, False)
    except AttributeError, e:
        debug(e)

class PyMpdGUI(object):
    def __init__(self, base, pympdclient):
        self.dir = Dir(base)
        self.pympd = pympdclient
        self.xml = gtk.glade.XML(self.dir.glade+"pympd.glade")
        self.get_widgets()
        self.initSelections()
        self.initBools()
        self.initStatusVars()
        self.initLists()
        self.buildHDTreeStore()
        self.buildSourceListStore()
        self.buildSongListStore()
        self.buildTrackListStore()
        self.buildArtistListStore()
        self.buildAlbumListStore()
        self.buildActionListStore()
        self.buildKeyListStore()
        self.buildPlaylistListStore()
        self.buildPluginListStore()
        self.buildPlaylistPrefStore()

#        self.startMpd()
        self.initWidgets()
        self.initPreferenceVars()
        self.connect_signal_handlers()
#        gobject.timeout_add(400, self.statusUpdate)
#        loadPsyco()
        self.pympdStatus = None

    def start(self):
        gtk.gdk.threads_init()
        gtk.gdk.threads_enter()
        gtk.main()
        gtk.gdk.threads_leave()

    def readConfigParser(self, filename):
        self.parser = ConfigParser.SafeConfigParser()
        if not os.path.exists(filename):
            debug("pympd.conf not found, creating a default", False)
            path = os.path.expanduser('~/.pympd')
            if not os.path.exists(path):
                os.mkdir(path)
            return
        self.parser.read(filename)
        # iterate over all our configuration options and read them. (maybe should just read relevant ones?)
        for key in config:
            try:
                config[key] = self.parser.get("options", key)
            except:
                print "%s does not exist," % key

        # read our keybindings
        if self.parser.has_section("keybindings"):
            for key in self.parser.options("keybindings"):
                keybindings[key] = self.parser.get("keybindings", key)


        if get_bool('autoconnect'):
            self.prefConnAuthCheckbox.set_active(get_bool('autoconnect'))
            self.prefConnPassEntry.set_text(get('password'))
            self.prefConnHostEntry.set_text(get('host'))
            self.prefConnPortEntry.set_text(str(get('port')))
            self.initMpd()
            gobject.timeout_add(int(get('timeout'))*1000, self.autoConnect)

        try:
            plugins = config['plugins'].split(',')
            for plugin in plugins:
                if not plugin.strip() == '':
                    self.loadPlugin(plugin.strip())
        except KeyError:
            pass


    def readConfig(self, filename):
        if not os.path.exists(filename):
            debug("pympd.conf not found, creating a default", False)
	    path = os.path.expanduser('~/.pympd')
	    if not os.path.exists(path):
		os.mkdir(path)
            return
        conf_file = open(filename, 'r')
        lines = conf_file.readlines()
        conf_file.close()
        self.config = {}
        for line in lines:
            line = line.strip()
            if (line == ""):
                continue
            if line[0] == '#':
                continue
            pref, value = sepAtColon(line)
            set(pref, value) # set the pref = value in config{}
        # now act on what we read
        if get_bool('autoconnect'):
            self.prefConnAuthCheckbox.set_active(get_bool('autoconnect'))
            self.prefConnPassEntry.set_text(get('password'))
            self.prefConnHostEntry.set_text(get('host'))
            self.prefConnPortEntry.set_text(str(get('port')))
            self.initMpd()
            gobject.timeout_add(int(get('timeout'))*1000, self.autoConnect)
        if config.has_key('width') and config.has_key('height'):
            self.mainWindow.resize(int(get('width')), int(get('height')))
        if config.has_key('plugins'):
            plugins = config['plugins'].split(',')
            for plugin in plugins:
                if not plugin.strip() == '':
                    self.loadPlugin(plugin.strip())


#    def saveConfig(self, filename):
#        conf_file = open(filename, 'w')
#        keys = config.keys()
#        lines = ''
#        if config.has_key('save_size'):
#            save_size = get_bool('save_size')
#        for key in keys:
#            if key == 'height' or key == 'width':
#                if save_size == False:
#                    continue
#            lines = lines + str(key + ':' + str(get(key)) + '\n')
#
#        conf_file.write(lines)
#        conf_file.close()

    def saveConfigParser(self, filename):
#        self.parser = ConfigParser.SafeConfigParser()
        if not self.parser.has_section("options"):
            self.parser.add_section("options")
        if not self.parser.has_section("keybindings"):
            self.parser.add_section("keybindings")

        for key in config:
            self.parser.set("options", key, str(config[key]))
        for key in keybindings:
            self.parser.set("keybindings", key, str(keybindings[key]))

        self.parser.write(open(filename, 'w'))

    def saveVisColumns(self):
        try:
            del config['visible_columns']
            del config['ordered_columns']
        except Exception, e:
            debug(str(e))

        columns = ''
        for column in self.songColumns.values():
            try:
                if self.visColumns[column]:
                    columns += column.get_title().lower() +','
            except KeyError:
                return
        config['visible_columns'] = columns


        order = ''
        for x in xrange(len(self.songColumns)):
            order += self.songView.get_column(x).get_property('title') + ','

        config['ordered_columns'] = order




    def savePlugins(self):
        try:
            del config['plugins']
        except Exception, e:
            debug(str(e))

        plugins = self.plugins.copy()
        pluginstring = ''
        for plugin in plugins:
            pluginstring += plugin +','
            self.unloadPlugin(plugin)
        config['plugins'] = pluginstring


    def saveWindowSize(self):
            x, y = self.mainWindow.get_size()
            p = self.topBox.get_position()
            set('width', x)
            set('height', y)
            set('pane_divider', p)

    def statusUpdate(self, data=None):
        if self.connected and not self.isAlive:
            try:
                self.pympdStatus = self.pympd.status()
                if self.didSongChange(self.pympdStatus):
                    self.updatePlaylist()
                    if get_bool('followsong'):
                        self.playlist_jump_to_song()
                elif self.didPlaylistChange(self.pympdStatus):
                    self.updateSongStore()
                self.updateUI(self.pympdStatus)
            except Exception, e:
                debug("%s %s" % (Exception, str(e)))
                self.connected = False
                self.statusLabel.set_text("Connection Failed")
                self.songStore.clear()
                self.updatePlaylist()
                self.correctConnectionButtons()
                self.updateLabels(False)
                self.updateTitle()
                return False
        self.spinPlugins(self.pympdStatus, self.songChanged)
        return True

    def autoConnect(self):
        if not self.connected:
            self.initMpd()
        if not int(get('timeout')) == self.prefConnTimeoutSpinner.get_value_as_int():
            set('timeout', self.prefConnTimeoutSpinner.get_value_as_int())
            gobject.timeout_add(int(get('timeout'))*1000, self.autoConnect)
            return False
        return get_bool('autoconnect')


    def updateBarFunc(self, thread, funcs):
        isAlive = thread.isAlive()
        if isAlive:
            self.updateBar.pulse()
        else:
            if not funcs == None:
                for func in funcs:
                    apply(func, ())
            #self.updateBar.set_fraction(1)
            self.updateBar.hide()
        self.isAlive = isAlive
        return self.isAlive

    def threadUpdateBar(self, thread, funcs=None):
        self.updateBar.show()
        gobject.timeout_add(60, self.updateBarFunc, thread, funcs)

# STATUS

    def didSongChange(self, status):
        if not status.songid == self.songID:
            self.lastSongID = self.songID
            self.songID = status.songid
            self.songChanged = True
        else:
            self.songChanged = False
        return self.songChanged

    def didPlaylistChange(self, status):
        if not status.playlist == self.playlist:
            self.playlist = status.playlist
            self.playlistChanged = True
            return True
        else:
            self.playlistChanged = False
            return False

    def updateUI(self, status):
        if get_bool('filter_playlist'):
            self.filterTracklist()
        if get_bool('filter_database'):
            self.filterPlaylist()
        self.updatePlayIcon()
        self.updateLabels()
        self.updateTitle()
        self.updateSlider(status)
        self.updateVolume(status)
        self.updateToggles(status)
        self.updateUpdater(status)

    def updateUpdater(self, status):
        if status.updating_db > 0:
            self.updateBar.show()
            self.updateBar.pulse()
            self.updateDBButton.set_property('sensitive', False)
            self.updating = 0
        else:
            if self.updating  > -1:
                self.updating = -1
                self.updateStats()
                #self.updateBar.set_fraction(1)
                self.updateBar.hide()
                self.alltrackStoreBuilt = False
                self.hdStoreBuilt = False
                self.updateDBButton.set_property('sensitive', True)
                self.updateArtistListStore()
        return

    def updateToggles(self, status):
        if not self.repeat == status.repeat:
            if not status.repeat == -1:
                self.repeatToggle.set_active(status.repeat)
                self.repeat = status.repeat
        if not self.shuffle == status.random:
            if not status.random == -1:
                self.shuffleToggle.set_active(status.random)
                self.shuffle = status.random
        if not get('crossfade') == status.xfade and int(status.xfade) > 0:
            self.crossfadeToggle.set_active(status.xfade)
            # Block the spinner from emitting a signal when we (pympd) changes it.
            # It will emit a signal when a user changes it though
            self.setting_xfade = True
            self.prefServerXFadeSpinner.set_value(int(status.xfade))
            self.setting_xfade = False
        return

    def filterSong(self, model, iter, data=None):
        lower = str.lower
        filter = lower(data.get_text().strip())
        if filter == "":
            return True
        # 0=artist, 1=album, 3=track-title
        for n in [0,1,3]:
            value = model.get_value(iter, n)
            if value:
#                value = value.lower()
                if lower(value).find(filter) > -1:
                    return True
        return False

    def filterTrack(self, model, iter, data=None):
        filter = self.searchTrackEntry.get_text().strip().lower()
        if filter == "":
            return True
        for n in xrange(1,4):
            value = model.get_value(iter, n)
            if not value == None:
                value = value.lower()
                if value.find(filter) > -1:
                    return True
        return False

    def filterTracklist(self, *args):
        filter = self.searchTrackEntry.get_text()
        if not filter == self.trackfilter:
            self.trackfilter = filter
            if self.alltracks:
                self.alltrackFilter.refilter()
                self.trackView.set_model(self.alltrackFilter)
            else:
                self.trackFilter.refilter()
                self.trackView.set_model(self.trackFilter)
        return

    def filterPlaylist(self):
        filter = self.searchSongEntry.get_text()
        if not filter == self.songfilter:
            self.songfilter = filter
            self.songFilter.refilter()
            self.songView.set_model(self.songSort)
        return

    def updatePlaylist(self):
        iter = self.songStore.get_iter_first()
        try:
            while iter:
                id = self.songStore.get_value(iter, 6)
                if int(id) == int(self.lastSongID):
                    for n in xrange(5):
                        value = stripBold(self.songStore.get_value(iter, n))
                        self.songStore.set_value(iter, n, value)
                if int(id) == int(self.songID):
                    for n in xrange(5):
                        value = stripBold(self.songStore.get_value(iter, n))
                        self.songStore.set_value(iter, n, makeBold(value))

                iter = self.songStore.iter_next(iter)
        except Exception, e:
            debug("%s %s" % (Exception, e))

        self.songView.set_model(self.songSort)
        return


    def updateLabels(self, force=None):
        if (self.songChanged or force or self.compact or self.playlistChanged) and self.connected:
            if not self.linkBox.get_property('visible') and not self.compact:
                self.linkBox.show()
            song = self.getSongInfo()
            if len(song) == 0:
                return
            title = song[1]
            artist = song[2]
            album = song[3]
            if title.strip() == "":
                title = song[4]

            if self.compact:
                title = '*** %s - %s '  % (artist, title)
                self.marqueeEntry.set_text(title)
                if self.songChanged or self.playlistChanged:
                    self.marqueeEntry.reset()
                return

            self.titleLabel.set_markup("<b><span size='xx-large'>%s</span></b>"%iAmp(title))
            self.albumLabel.set_markup("<b><span size='large'>%s</span></b>"%album)
            self.artistLabel.set_markup("<b><span size='large'>%s</span></b>"%artist)
        elif force == False:
            self.titleLabel.set_label("<b><big>Not Connected</big></b>")
            self.linkBox.hide()
            return
        elif self.stateStr == 'stop':
            self.titleLabel.set_label("<big><b>Stopped</b></big>")
            self.albumLabel.set_label("Album")
            self.artistLabel.set_label("Artist")
            return

    def updateTitle(self):
        if self.songChanged or self.playlistChanged and not self.stateStr == 'stop':
            song = self.pympd.getCurrentSong()
            title = 'pympd: %s - %s' % (song.artist, song.title)
            self.mainWindow.set_title(title)
        elif self.stateStr == 'stop' or not self.connected:
            title = 'pympd'
            self.mainWindow.set_title(title)

    def updateSlider(self, status):
        if not status.state == 'stop':
            if not self.sliding:
                if self.elapsedTime == status.elapsedTime and self.totalTime == status.totalTime:
                    return
                if self.songChanged or (self.elapsedTime == -1 and self.totalTime == -1):
                    self.sliderAdj.set_all(status.elapsedTime, 0, status.totalTime)
                else:
                    self.sliderAdj.set_value(float(self.elapsedTime))
                self.slider.set_adjustment(self.sliderAdj)

            # update slider label, even if slider is currently clicked
            self.totalTime = status.totalTime
            self.elapsedTime = status.elapsedTime
            self.convElapsed = secToString(self.elapsedTime)
            self.convTotal = secToString(self.totalTime)
            if not self.compact:
                self.sliderLabel.set_label("<span weight='bold'>%s of %s</span>" \
                        % (self.convElapsed, self.convTotal))

        else:
            self.sliderAdj.set_value(0)

    def updateVolume(self, status):
        if not self.volume == status.volume:
            self.volumeSliderAdj.set_value(status.volume)
            self.volumeScale.set_adjustment(self.volumeSliderAdj)
            self.volumeTooltip.set_tip(self.volumeScale, "%s%%"%status.volume)
            self.volume = status.volume

    def updateStats(self):
        stats = self.pympd.getStats()
        db_playtime = int(stats.db_playtime)
        db_play_days = db_playtime / 86400
        db_play_hours = db_playtime % 86400 / 3600
        db_play_minutes = db_playtime % 3600 / 60
        db_play_seconds = db_playtime % 60
        db_playtime = "%s days %s hours and %s minutes" % (db_play_days, db_play_hours, db_play_minutes);
#       db_playtime = str(db_play_days) + " days, " + str(db_play_hours) + " hours and " + str(db_play_minutes) + " minutes"
        db_update = "Update MPD Database\n(Last updated: " + time.strftime("%b %d, %Y at %H:%M", time.localtime(float(stats.db_update))) + ")"
        display = "%s songs. %s"%(stats.songs, db_playtime)
        tooltip = gtk.tooltips_data_get(self.updateDBButton)[0]
        tooltip.set_tip(self.updateDBButton, db_update)
        self.statsLabel.set_label(display)


# INIT FUNCTIONS

    def get_widgets(self):
        self.mainWindow = self.xml.get_widget("mainWindow")


        self.menuBar = self.xml.get_widget("menubar1")
        self.menuBarToggle = self.xml.get_widget("view_menubar")
        self.sideBarToggle = self.xml.get_widget("view_sidebar")
        self.browserbox = self.xml.get_widget("browserHBox")
        self.browserExpander = self.xml.get_widget("browserExpander")
        self.browserVBox = self.xml.get_widget("browser_vbox")
        self.playlistBox = self.xml.get_widget("vbox12")
        self.leftControlBox = self.xml.get_widget("hbox2")
        self.sliderBox = self.xml.get_widget("vbox3")
        self.hdBox = self.xml.get_widget("hdbox")
        self.libraryBox = self.xml.get_widget("libraryBox")
        self.hdView = self.xml.get_widget("hdview")
        self.sourceView = self.xml.get_widget("sourceTreeView")
        self.playlistView = self.xml.get_widget("playlistChooserTreeView")
        self.trackView = self.xml.get_widget("treeview4")
        self.songView = self.xml.get_widget("playlistTreeView")
        self.artistView = self.xml.get_widget("treeview6")
        self.albumView = self.xml.get_widget("treeview7")
        self.albumView = self.xml.get_widget("treeview7")
        self.playIcon = self.xml.get_widget("image7")
        self.slider = self.xml.get_widget("hscale1")
        self.sliderLabel = self.xml.get_widget("label6")
#        self.marqueeEntry = self.xml.get_widget("marqueeEntry")
        self.marqueeEntry = ticker.Ticker(50)
        self.marqueeParent = self.xml.get_widget("vbox5")
        self.topHSeparator = self.xml.get_widget("hseparator1")
        self.titleVBox = self.xml.get_widget("vbox3")
        self.titleLabel = self.xml.get_widget("label2")
        self.linkBox = self.xml.get_widget("hbox3")
        self.albumLabel = self.xml.get_widget("albumLabel")
        self.artistLabel = self.xml.get_widget("artistLabel")
        self.statsLabel = self.xml.get_widget("label1")
        self.prevButton = self.xml.get_widget("button7")
        self.playButton = self.xml.get_widget("button8")
        self.nextButton = self.xml.get_widget("button9")
        self.repeatToggle = self.xml.get_widget("repeat_button")
        self.shuffleToggle = self.xml.get_widget("shuffle_button")
        self.crossfadeToggle = self.xml.get_widget("crossfade_button")
        self.searchBox = self.xml.get_widget("searchbox")
        self.libraryViewCombobox = self.xml.get_widget("library_view_combobox")
        self.searchSongEntry = self.xml.get_widget("searchentry")
        self.searchTrackEntry = self.xml.get_widget("entry1")
        self.updateDBButton = self.xml.get_widget("updateDB_button")
        self.updateBar = self.xml.get_widget("progressbar1")
        self.statusLabel = self.xml.get_widget("status_label")
        self.statusBar = self.xml.get_widget("hbox1")
        self.sideBar = self.xml.get_widget("vbox9")
#        self.extendedBox = self.xml.get_widget("extendedBox")
#        self.notebook = self.xml.get_widget("notebook")
        self.controlBox = self.xml.get_widget("hbox6")
        self.playlistBox = self.xml.get_widget("vbox12")
        self.topBox = self.xml.get_widget("extendedBox")
        self.extendedParent = self.xml.get_widget("extendedParent")
        self.playlistParent = self.xml.get_widget("vbox10")
        self.browserParent = self.xml.get_widget("vbox10")
        self.externPlaylist = self.xml.get_widget("externPlaylist")
        self.playlistWindow = self.xml.get_widget("externPlaylistWindow")
        self.pluginWindow = self.xml.get_widget("externPluginWindow")
        self.externBrowser = self.xml.get_widget("externBrowser")
        self.browserWindow = self.xml.get_widget("externBrowserWindow")

        self.pluginMenuItem = self.xml.get_widget("plugins1")

        ### right click popup widgets ###
        self.browser_menu = self.xml.get_widget("browser_menu")
        self.playlist_menu = self.xml.get_widget("playlist_menu")
        self.playlist_chooser_menu = self.xml.get_widget("playlist_chooser_menu")

        ### preferences dialog widgets ###
        self.prefsDialog = self.xml.get_widget("preferencesDialog")
        self.prefConnHostEntry = self.xml.get_widget("pref_conn_host")
        self.prefConnPortEntry = self.xml.get_widget("pref_conn_port")
        self.prefConnAuthCheckbox = self.xml.get_widget("pref_conn_useAuth")
        self.prefConnPassEntry = self.xml.get_widget("pref_conn_pass")
        self.prefConnTimeoutSpinner = self.xml.get_widget("pref_conn_timeout")
        self.prefConnAutoCheckbox = self.xml.get_widget("pref_conn_auto")
        self.prefServerXFadeSpinner = self.xml.get_widget("pref_server_xfade")
        self.prefClientDblClickEnqueue = self.xml.get_widget("pref_client_dblClick_enqueue")
        self.prefClientDblClickReplace = self.xml.get_widget("pref_client_dblClick_replace")
        self.prefDispAll = self.xml.get_widget("pref_client_dbALL")
        self.prefStopQuit = self.xml.get_widget("pref_client_stopQuit")
        self.prefDebug = self.xml.get_widget("pref_client_debug")
        self.disconnectButton = self.xml.get_widget("disconnect_btn")
        self.connectButton = self.xml.get_widget("connect_btn")
        self.playlistPrefView = self.xml.get_widget("playlistPrefView")
        self.keyView = self.xml.get_widget("keyView")

        ### information dialog widgets ###
        self.infoDialog = self.xml.get_widget("trackinfoDialog")
        self.infoArtist = self.xml.get_widget("entry6")
        self.infoAlbum = self.xml.get_widget("entry7")
        self.infoTitle = self.xml.get_widget("entry8")
        self.infoTrack = self.xml.get_widget("entry10")
        self.infoBitrate = self.xml.get_widget("label57")
        self.infoLength = self.xml.get_widget("label58")
        self.infoFile = self.xml.get_widget("entry5")
        self.infoNext = self.xml.get_widget("info_next_button")
        self.infoPrev = self.xml.get_widget("info_prev_button")

        ### plugin dialog widgets ###
        self.pluginDialog = self.xml.get_widget("pluginDialog")
        self.pluginView = self.xml.get_widget("pluginView")
        self.pluginConf = self.xml.get_widget("plugins_conf_button")

        ### About Dialog ###
        self.aboutDialog = self.xml.get_widget("aboutDialog")

        ### Playlist Save Dialog ###
        self.playlistSaveDialog = self.xml.get_widget("playlistSaveDialog")
        self.playlistSaveEntry = self.xml.get_widget("entry11")

        ### Volume Slider Popup ###
        self.volumeButton = self.xml.get_widget("volumeButton")
        self.volumeWindow = self.xml.get_widget("volumeWindow")
        self.volumeScale = self.xml.get_widget("volumeScale")

        ### Menu Items ###
        self.menuConnect = self.xml.get_widget("menu_connect")
        self.menuDisconnect = self.xml.get_widget("menu_disconnect")
        self.playlistChooserEnqueue = self.xml.get_widget("playlist_chooser_enqueue")
        self.playlistChooserPlay = self.xml.get_widget("playlist_chooser_play")
        self.playlistChooserDelete = self.xml.get_widget("playlist_chooser_delete")
        self.playlistChooserSeparator = self.xml.get_widget("playlist_chooser_separator")
        return

    def initSelections(self):
        self.hdSelection = self.hdView.get_selection()
        self.artistSelection = self.artistView.get_selection()
        self.albumSelection = self.albumView.get_selection()
        self.sourceSelection = self.sourceView.get_selection()
        self.playlistSelection = self.playlistView.get_selection()
        self.trackSelection = self.trackView.get_selection()
        self.pluginSelection = self.pluginView.get_selection()
        self.songSelection = self.songView.get_selection()
        self.keySelection = self.keyView.get_selection()
        self.songSelection.set_mode(gtk.SELECTION_MULTIPLE)
        self.trackSelection.set_mode(gtk.SELECTION_MULTIPLE)
        self.artistSelection.set_mode(gtk.SELECTION_MULTIPLE)
        self.albumSelection.set_mode(gtk.SELECTION_MULTIPLE)
        self.hdSelection.set_mode(gtk.SELECTION_MULTIPLE)
        self.artistSelection.set_select_function(self.artist_selected, None)
        self.albumSelection.set_select_function(self.album_selected, None)
        self.sourceSelection.set_select_function(self.source_selected, None)
        self.playlistSelection.set_select_function(self.playlist_selected, None)
        self.pluginSelection.set_select_function(self.plugin_selected, None)
        self.browserSelection = None

        self.songView.connect('row-activated', self.song_activated)
        self.trackView.connect('row-activated', self.track_activated)
        self.albumView.connect('row-activated', self.album_activated)
        self.artistView.connect('row-activated', self.artist_activated)
        self.hdView.connect('row-activated', self.file_activated)

    def initBools(self):
        self.artistSelected = False
        self.albumSelected = False
        self.sliding = False
        self.volume_sliding = False
        self.compact = False
        self.reverse = False
        self.alltrackStoreBuilt = False
        self.hdStoreBuilt = False
        self.alltracks = False
        self.isAlive = False
        self.menu_opened = False
        self.connected = False
        self.songChanged = False
        self.statusbarVis = True
        self.sourceOnce = False
        self.key_choose = False

    def initLists(self):
        self.artists = None
        self.albums =None
        self.songs = None
        self.plugins = {}
        self.visColumns = {}
        self.modules = {}

    def initStatusVars(self):
        self.updating = -1
        self.source = None
        self.playlist = None
        self.songID = -1
        self.lastSongID = -1
        self.playlist = -1
        self.lastsongID = -1
        self.elapsedTime = -1
        self.totalTime = -1
        self.volume = -1
        self.shuffle = -1
        self.repeat = -1
        self.songfilter = ""
        self.trackfilter = ""
        self.scroll = 0
        self.focused = -1
        self.stateStr = ''
        self.oldplaylist = 0

    def initPreferenceVars(self):
        set('autoconnect', FALSE)
        set('browser', 'gnome-default')
        set('browser_action', '0')
        set('crossfade', 0)
        set('db_all', FALSE)
        set('debug', FALSE)
        set('double_click', "replace")
        set('filter_database', TRUE)
        set('filter_playlist', FALSE)
        set('height', '0')
        set('host', "localhost")
        set('password', "")
        set('playlist_open', FALSE)
        set('plugins', '')
        set('plugins_open', FALSE)
        set('port', 6600)
        set('save_size', -1)
        set('scrollwidth', 20)
        set('followsong', TRUE)
        set('stop_quit', FALSE)
        set('timeout', 1)
        set('use_auth', FALSE)
        set('visible_columns', 'artist,#,title,album')
        set('ordered_columns', 'Title,Album,Artist,#,Length,Filename')
        set('width', '0')
        set('pane_divider', -1)
        set('sticky', FALSE)
        set('alwaysontop', FALSE)
        set('decorated', TRUE)
        set_bool('compact', False)
        self.setting_xfade = False

        # default shortcuts
#        dict = {
#            'ctrl_p' : 'play_event',
#            'ctrl_s' : 'shuffle_clicked',
#            'ctrl_r' : 'repeat_clicked',
#            'ctrl_f' : 'focus_filter',
#            'ctrl_e' : 'browser_add',
#            'ctrl_d' : 'playlist_remove',
#            'ctrl_m' : 'compact_event',
#            'ctrl_i' : 'playlist_info',
#            'ctrl_l' : 'toggle_view_plugins'
#        }
#        for key in dict:
#            keybindings[key] = dict[key]

        try:
            self.readConfigParser(CONFIG_FILE)
        except:
            self.readConfig(CONFIG_FILE)
        # resize window and compact it
#        if get_bool('compact'):
#            self.compact_event(None)

        width, height = [int(x) for x in (get('width'), get('height'))]
        pane_position = int(get('pane_divider'))

        if pane_position > -1:
            self.topBox.set_position(pane_position)

        if width > 0 and height > 0:
            self.mainWindow.resize(width, height)

        # Reorder songview columns
        for value in get('ordered_columns').split(','):
            try:
                value = value.strip()
                if value == '':
                    continue
                self.songView.append_column(self.songColumns[value])
            except Exception, e:
                debug(e)
        
        # Pop any in, if we missed them.
        for column in self.songColumns.values():
            if not column in self.songView.get_columns():
                self.songView.append_column(column)
                


#        self.mainWindow.resize(int(get('width')), int(get('height')))


        # init the preference widgets
        self.prefConnHostEntry.set_text(get('host'))
        self.prefConnPortEntry.set_text(str(get('port')))
        self.prefConnAutoCheckbox.set_active(get_bool('autoconnect'))
        self.prefConnTimeoutSpinner.set_value(int(get('timeout')))
        self.prefDispAll.set_active(get_bool('db_all'))
        self.prefDebug.set_active(get_bool('debug'))
        self.prefStopQuit.set_active(get_bool('stop_quit'))
	self.prefConnAuthCheckbox.set_active(get_bool('use_auth'))
        # Set our browser menu item properly.
        self.xml.get_widget("view_browser").set_property('sensitive', False)
        self.xml.get_widget("view_browser").set_property('active', get_bool('playlist_open'))
        self.xml.get_widget("view_plugins").set_property('sensitive', False)
        self.xml.get_widget("view_plugins").set_property('active', get_bool('plugins_open'))
        self.xml.get_widget("filterdb_button").set_property('active', get_bool('filter_playlist'))
        self.xml.get_widget("filterpl_button").set_property('active', get_bool('filter_database'))
        self.xml.get_widget("followsong_button").set_property('active', get_bool('followsong'))
        self.xml.get_widget("stickytoggle").set_property('active', get_bool('sticky'))
        self.xml.get_widget("decortoggle").set_property('active', get_bool('decorated'))
        self.xml.get_widget("toptoggle").set_property('active', get_bool('alwaysontop'))
	self.prefConnPassEntry.set_text(get('password'))

        if get('double_click') == 'replace':
            self.prefClientDblClickReplace.set_active(True)
        else:
            self.prefClientDblClickEnqueue.set_active(True)

	if get_bool('use_auth'):
	    self.prefConnPassEntry.set_property('sensitive', True)

    # Set default widget values
    def initWidgets(self):
        self.searchBox.set_active(0)
        self.libraryViewCombobox.set_active(0) #TODO: remember user setting
        # the slider adjustment bars for volume/seeking
        self.sliderAdj = gtk.Adjustment()
        self.volumeSliderAdj = gtk.Adjustment(0, 0, 100)
        # tooltips for our volume
        self.volumeTooltip = gtk.Tooltips()

        # set row height at a constant, should speed up display time:
        self.trackView.set_property('fixed-height-mode', True)
        self.songView.set_property('fixed-height-mode', True)

        # build our plugin menu
        managePlugins = gtk.MenuItem('Manage Plugins')
        managePlugins.connect('activate', self.plugins_show)
        separator = gtk.SeparatorMenuItem()
        self.pluginMenu = gtk.Menu()
        self.pluginMenu.set_title('Plugins')
        self.pluginMenuItem.set_submenu(self.pluginMenu)
        self.pluginMenu.append(managePlugins)
        self.pluginMenu.append(separator)
        self.pluginMenu.show_all()

        # Use our brand new ticker widget and pack it in
        self.marqueeEntry.set_scroll(False)
        self.marqueeParent.pack_end(self.marqueeEntry, True)
        self.marqueeParent.show_all()

        try:
            self.aboutDialog.set_transient_for(self.mainWindow)
            self.prefsDialog.set_transient_for(self.mainWindow)
            self.infoDialog.set_transient_for(self.mainWindow)
            self.playlistSaveDialog.set_transient_for(self.mainWindow)
            self.pluginDialog.set_transient_for(self.mainWindow)
        except Exception, e:
            debug(str(e))

    def startMpd(self):
        return
        if get_bool('start_mpd'):
            command = "mpd %s" % (get('mpd_conf'))
            ret = os.spawnl(os.P_WAIT, command, "")
        return

    def initMpd(self):
        if self.prefConnAuthCheckbox.get_active():
            set('password', self.prefConnPassEntry.get_text())
            password = get('password')
        else:
            password = None

        if self.pympd._connect(get('host'), int(get('port')), password):
            self.statusLabel.set_text("Connected")
            self.connected = True
            self.correctConnectionButtons()
            self.updateStats()
            self.initListStores()
            self.updateLabels(True)
            self.playlist = -1
            self.alltrackStoreBuilt = False
            self.hdStoreBuilt = False
            gobject.timeout_add(500, self.statusUpdate)
        else:
            self.statusLabel.set_text("Connection Failed")

    def correctConnectionButtons(self):
        if self.connected == True:
            self.connectButton.set_property('sensitive',False)
            self.menuConnect.set_property('sensitive',False)
            self.disconnectButton.set_property('sensitive',True)
            self.menuDisconnect.set_property('sensitive',True)

        else:
            self.connectButton.set_property('sensitive',True)
            self.menuConnect.set_property('sensitive',True)
            self.disconnectButton.set_property('sensitive',False)
            self.menuDisconnect.set_property('sensitive',False)

    def initListStores(self):
        self.updateSourceListStore()
        self.updatePlaylistStore()
        self.updateArtistListStore()
        self.updatePluginListStore()
        self.updatePlaylistPrefStore()
        self.updateAlbumListStore()
        self.updateKeyListStore()

    def connect_signal_handlers(self):

        dic = { "on_main_destroy"             : self.quit_event,
                "on_main_configure"           : self.configure_event,
                "on_window_destroy"           : self.destroy_decoy,
                "on_browserExpander_activate" : self.expand_browser_event,
                "on_compact_activate"         : self.compact_event,
                "on_preferences1_activate"    : self.prefs_activate,
                "on_prefs_close_clicked"      : self.prefs_close,
                "on_play_clicked"             : self.play_event,
                "on_prev_clicked"             : self.prev_event,
                "on_next_clicked"             : self.next_event,
                "on_save_playlist_clicked"    : self.save_playlist,
                "on_delete_playlist_clicked"  : self.del_playlist,
                "on_append_playlist_clicked"  : self.append_playlist,
                "on_replace_playlist_clicked" : self.replace_playlist,
                "on_append_tracks_clicked"    : self.append_tracks,
                "on_replace_tracks_clicked"   : self.replace_tracks,
                "on_shuffle_clicked"          : self.shuffle_clicked,
                "on_showmenu_toggled"         : self.showmenu_toggled,
                "on_repeat_clicked"           : self.repeat_clicked,
                "on_crossfade_clicked"        : self.crossfade_clicked,
                "on_slider_changed"           : self.slider_changed,
                "on_slider_moved"             : self.slider_moved,
                "on_search_activated"         : self.filterTracklist,
                "on_update_clicked"           : self.update_event,
                "on_saveDialogSaveButton_clicked" : self.playlistDialogSave,
                "on_saveDialogCancelButton_clicked" : self.playlistDialogCancel,
                "on_aboutDialog_close"        : self.hide_about,
                "on_library_view_combobox_changed" : self.libraryViewChanged }
        self.xml.signal_autoconnect(dic)


        # preferences handlers
        dic = { "on_xfade_editing_done"       : self.set_xfade,
                "on_disconnect_clicked"       : self.disconnect_mpd,
                "on_connect_clicked"          : self.connect_mpd,
                "on_useAuth_toggled"          : self.useAuth_toggled,
                "on_conn_auto_toggled"        : self.conn_auto_toggled,
                "on_enqueue_toggled"          : self.enqueue_toggled,
                "on_dbALL_toggled"            : self.dbALL_toggled,
                "on_stopQuit_toggled"         : self.stopQuit_toggled,
                "on_debug_toggled"            : self.debug_toggled,
                "on_filterpl_toggled"         : self.filterpl_toggled, 
                "on_filterdb_toggled"         : self.filterdb_toggled,
                "on_followsong_toggled"       : self.followsong_toggled, 
                "on_toptoggle_toggled"        : self.ontop_toggled, 
                "on_stickytoggle_toggled"     : self.sticky_toggled,
                "on_decortoggle_toggled"      : self.decor_toggled, 
                }
        self.xml.signal_autoconnect(dic)

        # popup menu handlers
        dic = { "on_browser_clicked"          : self.browser_clicked,
                "on_browser_add"              : self.browser_add,
                "on_browser_replace"          : self.browser_replace,
                "on_browser_update"           : self.browser_update,
                "on_playlist_clicked"         : self.playlist_clicked,
                "on_playlist_chooser_clicked" : self.playlist_chooser_clicked,
                "on_playlist_remove"          : self.playlist_remove,
                "on_playlist_crop"            : self.playlist_crop,
                "on_playlist_clear"           : self.playlist_clear,
                "on_playlist_info"            : self.playlist_info}
        self.xml.signal_autoconnect(dic)

        # menubar handlers
        dic = { "on_view_browser_activate"    : self.change_view_browser,
                "on_view_plugins_activate"    : self.change_view_plugins,
                "on_view_sidebar_activate"    : self.change_view_sidebar,
                "on_view_menubar_activate"    : self.change_view_menubar,
                "on_view_statusbar_activate"  : self.change_view_statusbar,
                "on_jump_activate"            : self.playlist_jump_to_song,
                "on_about_activate"           : self.show_about,
                "on_menu_connect_activate"    : self.connect_mpd,
                "on_menu_disconnect_activate" : self.disconnect_mpd,
                "on_quit_activate"            : self.quit_event }
        self.xml.signal_autoconnect(dic)

        # information dialog handlers
        dic = { "on_info_next_clicked"        : self.info_next,
                "on_info_prev_clicked"        : self.info_prev,
                "on_info_close_clicked"       : self.info_close }
        self.xml.signal_autoconnect(dic)

        # external playlist handlers
        dic = { "on_playlist_add_clicked"     : self.playlist_add,
                "on_playlist_remove_clicked"  : self.playlist_remove,
                "on_playlist_info_clicked"    : self.playlist_info,
                "on_playlist_clear_clicked"   : self.playlist_clear,
                "on_playlist_close_clicked"   : self.playlist_close }
        self.xml.signal_autoconnect(dic)

        # plugin manager handlers
        dic = { "on_plugins_conf_clicked"     : self.plugins_conf,
                "on_plugins_ok_clicked"       : self.plugins_close }
        self.xml.signal_autoconnect(dic)

        # volume handlers
        dic = { "on_volumeButton_clicked"     : self.volume_slider_display,
#                "on_volumeScale_press"        : self.volume_slider_press,
#                "on_volumeScale_release"      : self.volume_slider_release, 
                "on_volumeScale_value_changed" : self.volume_slider_changed, 
                }
#                "on_volumeWindow_unfocus"     : self.volume_slider_undisplay}
        self.xml.signal_autoconnect(dic)

        # connect keybindings
        dic = {"key_press_event"              : self.key_press_event,
               "on_key_add_clicked"           : self.key_add_event,
               "on_key_save_clicked"          : self.key_save_event,
               "on_key_remove_clicked"        : self.key_remove_event }

        self.xml.signal_autoconnect(dic)
        return


    def buildHDTreeStore(self):
        # (icon name, display name, path)
        self.hdStore = gtk.TreeStore(str, str, str)
        column = gtk.TreeViewColumn()
        cellPB = gtk.CellRendererPixbuf()
        cellStr = gtk.CellRendererText()
        column.pack_start(cellPB, False)
        column.add_attribute(cellPB, 'icon-name', 0)
        column.pack_start(cellStr, True)
        column.set_attributes(cellStr, text=1)
        column.set_widget(None)
        self.hdView.append_column(column)
        self.hdView.set_model(self.hdStore)
        self.hdView.connect('row-expanded', self.dir_expanded)

    def buildSourceListStore(self):
        self.sourceStore = gtk.ListStore(str, str)
        self.sourceCol = gtk.TreeViewColumn('Source')
        self.sourceView.append_column(self.sourceCol)
        self.updateSourceListStore()
        cellStr = gtk.CellRendererText()
        cellPix = gtk.CellRendererPixbuf()
        self.sourceCol.pack_start(cellPix, False)
        self.sourceCol.pack_start(cellStr, True)
        self.sourceCol.set_attributes(cellStr, markup=1)
        self.sourceCol.set_attributes(cellPix, stock_id=0)

    def buildPlaylistListStore(self):
        self.playlistStore = gtk.ListStore(str, str)
        self.playlistCol = gtk.TreeViewColumn('Playlists')
        self.playlistView.append_column(self.playlistCol)
        cellStr = gtk.CellRendererText()
        cellPix = gtk.CellRendererPixbuf()
        cellStr.set_property('editable', True)
        cellStr.connect('edited', self.playlist_edited, None)
        self.playlistCol.pack_start(cellPix, False)
        self.playlistCol.pack_start(cellStr, True)
        self.playlistCol.set_attributes(cellStr, markup=1)
        self.playlistCol.set_attributes(cellPix, stock_id=0)

    def buildSongListStore(self):
        self.songStore = gtk.ListStore(str, str, str, str, str, str, str)
        self.songFilter = self.songStore.filter_new()
        self.songFilter.set_visible_func(self.filterSong, self.searchSongEntry)
        self.songSort = gtk.TreeModelSort(self.songFilter)
        colHeadings = ['Artist', 'Album', '#','Title', 'Length', 'Filename']
        self.songColumns = {}
        # TODO: remove hardcoded 3 for playlist
        self.sourceSelection.select_path((1, ))
        cellStr = gtk.CellRendererText()
        # Create the 5 columns in colheadings[]
        for n in xrange(6):
            self.songColumns[colHeadings[n]] = gtk.TreeViewColumn(colHeadings[n])
#            self.songView.append_column(self.songColumns[colHeadings[n]])
            self.songColumns[colHeadings[n]].pack_start(cellStr, True)
            self.songColumns[colHeadings[n]].set_attributes(cellStr, markup=n)
            self.songColumns[colHeadings[n]].set_sort_column_id(n)
            self.songColumns[colHeadings[n]].set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
            self.songColumns[colHeadings[n]].set_resizable(True)
            self.songColumns[colHeadings[n]].set_reorderable(True)
            self.songColumns[colHeadings[n]].set_visible(False)

        self.songColumns['Artist'].set_property('fixed-width', 175) # Artist
        self.songColumns['Album'].set_property('fixed-width', 200) # Album
        self.songColumns['#'].set_property('fixed-width', 30)  # #
        self.songColumns['Title'].set_property('fixed-width', 200) # Title
        self.songColumns['Length'].set_property('fixed-width', 50)  # Length
        self.songColumns['Filename'].set_property('fixed-width', 200) # Filename


    def buildTrackListStore(self):
        # the liststore holds information in a list of (track, title, artist, album, trackid)
        self.trackStore = gtk.ListStore(str, str, str, str, str)
        colHeadings = ['#', 'Title', 'Artist', 'Album']
        self.trackColumns = [None]*len(colHeadings)
        cellStr = gtk.CellRendererText()
        self.trackFilter = self.trackStore.filter_new()
        self.trackFilter.set_visible_func(self.filterTrack, None)
        self.trackSort = gtk.TreeModelSort(self.trackFilter)
        # Create the 4 columns in colheadings[]
        for n in xrange(len(colHeadings)):
            self.trackColumns[n] = gtk.TreeViewColumn(colHeadings[n])
            self.trackView.append_column(self.trackColumns[n])
            self.trackColumns[n].pack_start(cellStr, True)
            self.trackColumns[n].set_attributes(cellStr, text=n)
            self.trackColumns[n].set_sort_column_id(n)
            self.trackColumns[n].set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
            self.trackColumns[n].set_resizable(True)
        self.trackColumns[1].set_property('fixed-width', 200)
        self.trackColumns[2].set_property('fixed-width', 175)
        self.trackColumns[3].set_property('fixed-width', 200)
        self.trackColumns[0].set_property('fixed-width', 20)

    def buildAllTrackListStore(self):
        self.alltrackStore = gtk.ListStore(str, str, str, str, str)
        self.alltrackFilter = self.alltrackStore.filter_new()
        self.alltrackFilter.set_visible_func(self.filterTrack, None)
        self.trackView.set_model(None)
#        gtk.threads_enter()
#        songs = threadsafe(self.pympd.listallSongs, [None, None])
        songs = self.pympd.listallSongs(None, None)
#        gtk.threads_leave()
        append = self.alltrackStore.append
        # trying it without the loop. :/
#        map((lambda x: append((x.title, x.artist, x.album, x.file))), songs)
#        [append((x.title, x.artist, x.album, x.file)) for x in songs]
        attrs = ['artist', 'title', 'album', 'track']
        for song in songs:
            try:
#                for attr in attrs:
#                    if not song.has_key(attr):
#                        song[attr] = ''
                if song.title == '':
                    song.title = os.path.splitext(os.path.basename(song.file))[0]
                append((song.track, song.title, song.artist, song.album, song.file))
            except Exception, e:
                debug(str(e))
        self.alltrackStoreBuilt = True


    def buildArtistListStore(self):
        self.artistStore = gtk.ListStore(str)
        self.artistCol = gtk.TreeViewColumn('Artist')
        self.artistView.append_column(self.artistCol)
        cellStr = gtk.CellRendererText()
        self.artistCol.pack_start(cellStr, True)
        self.artistCol.set_attributes(cellStr, markup=0)
        self.artistStore.set_sort_func(4, self.alpha_sort, None)
        self.artistStore.set_sort_column_id(4, gtk.SORT_ASCENDING)

    def buildAlbumListStore(self):
        self.albumStore = gtk.ListStore(str)
        self.albumCol = gtk.TreeViewColumn('Album')
        self.albumView.append_column(self.albumCol)
        cellStr = gtk.CellRendererText()
        self.albumCol.pack_start(cellStr, True)
        self.albumCol.set_attributes(cellStr, markup=0)
        self.albumStore.set_sort_func(4, self.alpha_sort, None)
        self.albumStore.set_sort_column_id(4, gtk.SORT_ASCENDING)

    def buildPluginListStore(self):
        self.pluginStore = gtk.ListStore(str, bool)
        self.pluginColumns = [gtk.TreeViewColumn('Plugin'), gtk.TreeViewColumn('Load')]
        self.pluginView.append_column(self.pluginColumns[1])
        self.pluginView.append_column(self.pluginColumns[0])
        cellStr = gtk.CellRendererText()
        cellBool = gtk.CellRendererToggle()
        cellBool.connect('toggled', self.plugin_togglecb, None)
        self.pluginColumns[0].pack_start(cellStr, True)
        self.pluginColumns[0].set_attributes(cellStr, text=0)
        self.pluginColumns[1].pack_start(cellBool, True)
        self.pluginColumns[1].add_attribute(cellBool, 'active', 1)
        self.pluginColumns[1].set_cell_data_func(cellBool, self.plugin_cb)


    # For use with the combo box in the keyStore.
    def buildActionListStore(self):
        self.actionStore = gtk.ListStore(str)
        for action in actions: # actions is global
            self.actionStore.append([action])



    def buildKeyListStore(self):
        self.keyStore = gtk.ListStore(bool, bool, bool, str, str)
        self.keyColumns = [gtk.TreeViewColumn('Ctrl'), gtk.TreeViewColumn('Alt'),\
                gtk.TreeViewColumn('Shift'), gtk.TreeViewColumn('Key'), gtk.TreeViewColumn('Action')]
        for n in xrange(0, 3):
            cellBool = gtk.CellRendererToggle()
            cellBool.connect('toggled', self.keyview_togglecb, n)
            self.keyColumns[n].pack_start(cellBool, False)
            self.keyColumns[n].add_attribute(cellBool, 'active', n)

        cellStr = gtk.CellRendererText()
        self.keyColumns[3].pack_start(cellStr, True)
        self.keyColumns[3].set_attributes(cellStr, text=3)
        cellStr.set_property('editable', True)
        cellStr.connect('editing-started', self.keyview_key_edited, None)

        cellCombo = gtk.CellRendererCombo()
        self.keyColumns[4].pack_start(cellCombo, True)
        self.keyColumns[4].set_attributes(cellCombo, text=4)
        cellCombo.set_property('editable', True)
        cellCombo.set_property('model', self.actionStore)
        cellCombo.set_property('text-column', 0)
        cellCombo.connect('edited', self.keyview_action_edited, None)

        for n in xrange(0,5):
            self.keyView.append_column(self.keyColumns[n])


    def buildPlaylistPrefStore(self):
        self.playlistPrefStore = gtk.ListStore(bool, str, gtk.TreeViewColumn)
        self.playlistColumns = [gtk.TreeViewColumn('Visible'), gtk.TreeViewColumn('Column')]
        self.playlistPrefView.append_column(self.playlistColumns[0])
        self.playlistPrefView.append_column(self.playlistColumns[1])
        cellStr = gtk.CellRendererText()
        cellBool = gtk.CellRendererToggle()
        cellBool.connect('toggled', self.playview_togglecb, None)
        self.playlistColumns[0].pack_start(cellBool, True)
        self.playlistColumns[0].add_attribute(cellBool, 'active', 0)
        self.playlistColumns[0].set_cell_data_func(cellBool, self.playview_cb)
        self.playlistColumns[1].pack_start(cellStr, True)
        self.playlistColumns[1].set_attributes(cellStr, text=1)



# UI FUNCTIONS

    def updatePlayIcon(self):
        if self.connected:
            self.stateStr = self.pympdStatus.state
            if self.stateStr == 'play':
                self.playIcon.set_from_stock(gtk.STOCK_MEDIA_PAUSE, gtk.ICON_SIZE_LARGE_TOOLBAR)
            elif self.stateStr == 'pause' or 'stop':
                self.playIcon.set_from_stock(gtk.STOCK_MEDIA_PLAY, gtk.ICON_SIZE_LARGE_TOOLBAR)

    def browserActivate(self):
        active = True
        self.browserVBox.set_property('sensitive', active)
        if not self.focused == -1:
            self.mainWindow.set_focus(self.focused)
        return

    def browserDeActivate(self):
        active = False
        self.browserVBox.set_property('sensitive', active)
        return

    def updateHDStore(self):
        self.hdStore.clear()
        self.hdView.freeze_child_notify()
        readDir(None, None, self.hdStore, self.pympd)
        self.hdView.thaw_child_notify()
        self.hdStoreBuilt = True


    def updateSourceListStore(self):
        #will eventually grab playlists from mpd
        self.sourceStore.clear()
        self.sourceStore.prepend([gtk.STOCK_FILE, PLAYLIST])
        #self.sourceStore.prepend([gtk.STOCK_HARDDISK, DATABASE])
        #self.sourceStore.prepend([gtk.STOCK_HARDDISK, HARDDRIVE])
        self.sourceStore.prepend([gtk.STOCK_HARDDISK, LIBRARY])
        self.sourceView.set_model(self.sourceStore)
        return

    def updatePlaylistStore(self):
        #will eventually grab playlists from mpd
        self.playlistStore.clear()
        playlists = self.pympd.getPlaylistNames()
        for playlist in playlists:
            self.playlistStore.append([gtk.STOCK_FILE, playlist])
        self.playlistView.set_model(self.playlistStore)
        return

# Called by updateTrackListStore (it's safe for threads, too)
    def updateTrackListStoreHelper(self, artists=None, albums=None):
        if artists == None and albums == None:
            self.alltracks = True
            if not self.alltrackStoreBuilt:
#                profile(self.buildAllTrackListStore)
                self.buildAllTrackListStore()
                self.artistSelection.select_path((0, ))
            threadsafe(self.trackView.set_model, [self.alltrackFilter])
            return
        self.alltracks = False
        gtk.gdk.threads_enter()
        success = False
        while success != True:
            try:
                songs = self.pympd.listallSongs(artists, albums)
                success = True
            except:
                time.sleep(0.1)


        gtk.gdk.threads_leave()
        threadsafe(self.trackStore.clear)
        gtk.gdk.threads_enter()
        insert = self.trackStore.insert
        index = 0
#        [append((iAmp(s.title), iAmp(s.artist), iAmp(s.album), s.file)) for s in songs]
        attrs = ['artist', 'title', 'album', 'track']
        for song in songs:
            try:
#                for attr in attrs:
#                    if not song.has_key(attr):
#                        song[attr] = ''
                if song.title == '':
                    song.title = os.path.splitext(os.path.basename(song.file))[0]
                insert(index, (song.track, song.title, song.artist, song.album, song.file))
                index += 1
            except Exception, e:
                debug(str(e))
#        for s in songs:
#           append((s.track, iAmp(s.title) , iAmp(s.artist), iAmp(s.album), s.file))
        gtk.gdk.threads_leave()
        threadsafe(self.trackView.set_model, [self.trackSort])
        return

    def updateTrackListStore(self, artists=None, albums=None):
            self.browserDeActivate()
            self.addThread = threading.Thread(target=self.updateTrackListStoreHelper, args=[artists, albums])
            self.addThread.start()
            self.threadUpdateBar(self.addThread, [self.browserActivate])

    # load_items() implemented using plchanges() command.
    def load_items(self, treeview, liststore, playlist):
        self.songSelection.unselect_all() # Unselect all songs so when we update the playlist it doesn't look weird.

        # Find the length of our new playlist and crop out songs no longer in it.

        pindex = int(self.pympdStatus.playlistlength)
        if pindex < len(self.songStore):
            for x in xrange(pindex, len(self.songStore)):
                del self.songStore[pindex]


        # Find the new playlist id and get the changes since our last playlist.
        playlistid = int(self.pympdStatus.playlist)
        plchanges = self.pympd.plchanges(self.oldplaylist)

        n = 150
        esc = cgi.escape
        index = 0
        for s in plchanges:
            length = secToString(s.time)
            fillSong(s)
            if s['title'] == '':
                s['title'] = os.path.splitext(os.path.basename(s.file))[0]
            try:
                liststore[s.pos] = (iAmp(s.artist), iAmp(s.album), s.track, iAmp(s.title), length, s.file, s.id)
            except TypeError:
                liststore.insert(int(s.pos), (iAmp(s.artist), iAmp(s.album), s.track, iAmp(s.title), length, s.file, s.id))
            index += 1
            if index % n == 0:
                yield True
        # Set our playlist id to the new one
        self.oldplaylist = playlistid
        self.updatePlaylist()
        self.statusLabel.set_label("Connected")
        yield False

# load_items() implemented using playlist() command.
#    def load_items(self, treeview, liststore, playlist):
#        if playlist == None:
#            songs = self.pympd.playlist()
#        else:
#            songs = self.pympd.listallSongsInPlaylist(playlist)
#        index = 0
#        n = 150
#        insert = liststore.insert
#        liststore.clear()
#        esc = cgi.escape
##        treeview.set_model(None)
#        for s in songs:
#            length = secToString(s.time)
#            fillSong(s)
#            if s['title'] == '':
#                s['title'] = os.path.splitext(os.path.basename(s.file))[0]
#            insert(index, (s.track, esc(s.title), esc(s.artist), \
#                    esc(s.album), length, esc(s.file), s.id))
##            insert(index, (s.track, iAmp(s.title), iAmp(s.artist), iAmp(s.album), length, s.file, s.id))
#            index += 1
#            if index % n == 0:
#                yield True
##        treeview.set_model(liststore)
##        self.updatePlaylist()
#        self.statusLabel.set_label("Connected")
#        yield False

    def lazyLoadSongStore(self, playlist):
        gobject.idle_add(self.load_items(self.songView, self.songStore, playlist).next)#, priority=gobject.PRIORITY_HIGH_IDLE)

    def loadSongStore(self, playlist=None):
        self.statusLabel.set_label("Updating Playlist")
        self.songSelection.unselect_all() # Unselect all songs so when we update the playlist it doesn't look weird.
#        self.songStore.clear()


        # Find the length of our new playlist and crop out songs no longer in it.
        pindex = int(self.pympdStatus.playlistlength)
        if pindex < len(self.songStore):
            for x in xrange(pindex, len(self.songStore)):
                del self.songStore[pindex]
        elif pindex >= len(self.songStore):
            for x in xrange(len(self.songStore), pindex):
                self.songStore.insert(x, [None]*7)


        # Find the new playlist id and get the changes since our last playlist.
        playlistid = int(self.pympdStatus.playlist)
        plchanges = self.pympd.plchanges(self.oldplaylist)

        self.songView.freeze_child_notify()
        changed = 0
        for s in plchanges:
            length = secToString(s.time)
            fillSong(s)
            if s['title'] == '':
                s['title'] = os.path.splitext(os.path.basename(s.file))[0]
            self.songStore[s.pos] = (s.track, iAmp(s.title), iAmp(s.artist), iAmp(s.album), length, s.file, s.id)
            changed += 1
        # Set our playlist id to the new one
        self.oldplaylist = playlistid
        self.songView.thaw_child_notify()
#        self.songView.set_model(self.songSort)
        self.updatePlaylist()
        self.statusLabel.set_label("Connected")
#        print "Modified %s songs" % (changed)
        return

#        if playlist == None:
#            songs = self.pympd.playlist()
#        else:
#            songs = self.pympd.listallSongsInPlaylist(playlist)
#        self.songView.freeze_child_notify()
#        index = 0
#        insert = self.songStore.insert
#        for s in songs:
#            length = secToString(s.time)
#            fillSong(s)
#            if s['title'] == '':
#                s['title'] = os.path.splitext(os.path.basename(s.file))[0]
#            insert(index, (s.track, iAmp(s.title), iAmp(s.artist), iAmp(s.album), length, s.file, s.id))
#            index += 1
#        self.songView.thaw_child_notify()
#        self.songView.set_model(self.songSort)
#        self.updatePlaylist()
#        self.statusLabel.set_label("Connected")
#        return

    def updateSongStore(self, playlist=None):
        return self.lazyLoadSongStore(playlist)
#        self.loadSongStore(playlist)
#        return profile(self.loadSongStore)

    def updateArtistListStore(self):
        self.artistStore.clear()
        artists = self.pympd.listallArtists()
        for artist in artists:
#            if artist != None:
            try:
                artist = artist.strip()
                if artist == '':
                    continue
                self.artistStore.append([insertAmp(artist)])
            except Exception, e:
                debug("%s %s"%(Exception, e))
        self.artistStore.prepend([ALL])
        self.artistView.set_model(self.artistStore)
        return

    def updateAlbumListStore(self, artists=None):
        self.albumStore.clear()
        albums = self.pympd.listallAlbums(artists)
        for album in albums:
            if album != None:
                self.albumStore.append([insertAmp(album)])
        self.albumStore.prepend([ALL])
        self.albumView.set_model(self.albumStore)
        return

    def updatePluginListStore(self):
        self.pluginStore.clear()
        running_plugins = self.plugins.keys()
        for plugin in os.listdir(self.dir.plugin):
            plugin, ext = stripExt(plugin)
            if ext == 'py':
                if plugin in running_plugins:
                    running = True
                else:
                    running = False
                self.pluginStore.append((plugin, running))
        self.pluginView.set_model(self.pluginStore)


    #TODO: finish writing this.
    def updateKeyListStore(self):
        self.keyStore.clear()
        for key in keybindings:
            ctrl_mod = alt_mod = shift_mod = False
            try:
                action = action_defs[keybindings[key]]
            except KeyError, e:
                debug(e)
            modifiers = key.split('_')
            if len(modifiers) > 1:
                if 'ctrl' in modifiers:
                    ctrl_mod = True
                if 'alt' in modifiers:
                    alt_mod = True
                if 'shift' in modifiers:
                    shift_mod = True
            self.keyStore.append((ctrl_mod, alt_mod, shift_mod, modifiers[-1], action))
        self.keyView.set_model(self.keyStore)

    def updatePlaylistPrefStore(self):
        visColumnString = get('visible_columns')
        nowVisColumns = visColumnString.split(',')
        self.visColumns = {}
        self.playlistPrefStore.clear()
        for column in self.songColumns.values():
            if column.get_title().lower() in nowVisColumns:
                truth = True
            else:
                truth = False
            column.set_visible(truth)
            self.visColumns[column] = truth
            self.playlistPrefStore.append((truth, column.get_title(), column))
        self.playlistPrefView.set_model(self.playlistPrefStore)

    def getSongInfo(self, song=None, fixAmp=None):
        if song == None:
            song = self.pympd.getCurrentSong()
        if song == False:
            return ()
        artist = song.artist
        title = song.title
        album = song.album
        track = song.track
        filename = song.file
        if fixAmp:
            track = insertAmp(track)
            title = insertAmp(title)
            artist = insertAmp(artist)
            album = insertAmp(album)
        return track, title, artist, album, filename

    def getExtSongInfo(self, song=None):
        if song == None:
            song = self.pympd.getCurrentSong()
        artist = song.artist
        title = song.title
        album = song.album
        track = song.track
        filename = song.file
        id = song.id
        length = song.time
        return filename, artist, title, album, track, length, id

    def filter_clear(self, arg=None):
        self.searchSongEntry.set_text('')

    def plugins_show(self, obj):
        self.updatePluginListStore()
        self.pluginDialog.show()

    def plugins_conf(self, obj):
        model, iter = self.pluginSelection.get_selected()
        plugin = model.get_value(iter, 0)
        self.plugins[plugin]._conf()
        return

    def plugins_close(self, obj):
        self.pluginDialog.hide()

    def plugin_buttons(self, plugin):
        conf = False
        if self.plugins.has_key(plugin):
            if self.plugins[plugin]._conf(True):
                conf = True
        self.pluginConf.set_property('sensitive', conf)
        return

    def plugin_info(self, plugin):
        attrs = {}
        try:
            module = self.loadModule(plugin)
            for attr in ['name', 'version', 'author', 'blurb']:
                try:
                    attrs[attr] = getattr(module, "__%s" % (attr))
                except:
                    attrs[attr] = "Undefined"
        except Exception, e:
            debug(str(e))
            self.xml.get_widget("plNameLabel").set_text(plugin)
            attrs['name'] = plugin
            attrs['version'] = 'Unknown'
            attrs['author'] = 'Unknown'
            attrs['blurb'] ='An error occurred while trying to load this plugin\'s information'

        self.xml.get_widget("plNameLabel").set_text(attrs['name'])
        self.xml.get_widget("plVerLabel").set_text(attrs['version'])
        self.xml.get_widget("plAuthLabel").set_text(attrs['author'])
        self.xml.get_widget("plDescLabel").set_text(attrs['blurb'])

    def volume_slider_display(self, obj, redisplay=False):
        #do the sizing stufhf
        if not self.volumeWindow.get_property('visible') or redisplay:
            x_win, y_win = self.volumeButton.window.get_origin()
            button_rect = self.volumeButton.get_allocation()
            x_coord, y_coord = button_rect.x + x_win, button_rect.y+y_win
            width, height = button_rect.width, button_rect.height
            self.volumeWindow.set_size_request(width, -1)
            self.volumeWindow.move(x_coord, y_coord+height)
            self.volumeWindow.show()
        else:
            self.volume_slider_undisplay(obj, None)
        return

    def volume_slider_changed(self, obj): 
        new_volume = int(obj.get_adjustment().get_value())
        if self.volume != new_volume:
            self.pympd.volume(new_volume)

    def volume_slider_undisplay(self, obj, data):
        self.volumeWindow.hide()

# MPD FUNCTIONS

    #TODO: Must fix threading issues here.
    def addHelper(self, songs=None):
        # if the DATABASE view is opened
        if self.view == DATABASE:
            threadsafe(self.trackSelection.selected_foreach, [self.foreach_track_selected, None])
        # otherwise the harddrive view is opened
        elif self.view == HARDDRIVE:
            threadsafe(self.hdSelection.selected_foreach, [self.foreach_file_selected, None])

        if songs == None:
            songs = self.songs
        threadsafe(self.statusLabel.set_text, ["Added %s songs" % len(songs)])
#        threadsafe(self.pympd.add, [songs])
        self.pympd.add(songs)

    def add(self, songs=None, funcs=None):
        self.browserDeActivate()
        self.addThread = threading.Thread(target=self.addHelper, args=[songs])
        self.addThread.start()
        if not funcs == None:
            funcs.extend([self.browserActivate])
        else:
            funcs = [self.browserActivate]
        self.threadUpdateBar(self.addThread, funcs)

    def append(self, songs=None):
        self.add()
        return

    def replace(self, songs=None):
        self.pympd.clear()
        self.add(None, [self.pympd.play])
        pass

    def savePlaylist(self, playlist):
        self.playlistSaveDialog.present()

    def deletePlaylist(self, playlist):
        print "Deleting playlist", playlist
        self.pympd.rm(playlist)
        self.updatePlaylistStore()
        self.statusLabel.set_text("Deleted %s" % (playlist))
        return

    def appendPlaylist(self, playlist):
        self.pympd.load(playlist)
        self.statusLabel.set_text("Appended %s to Playlist" % (playlist))
        return

    def replacePlaylist(self, playlist):
        self.pympd.clear()
        self.pympd.load(playlist)
        self.statusLabel.set_text("Playing %s"%(playlist))

# KEYBINDINGS
# TODO: create a dictionary for each key event, eventually making them configurable.
# example: p:play_event means when 'p' is pressed, we call self.play_event() (make sure to
# properly pass parameters.
    def key_press_event(self, widget, event):
        keyname = gtk.gdk.keyval_name(event.keyval).lower()

        # If we are choosing keybindings, automatically grab the next key pressed
        if self.key_choose:
            if not keyname == 'escape':
                model = self.keyView.get_model()
                model.set(self.keyview_iter, 3, keyname)
            gtk.gdk.keyboard_ungrab(0)
            self.key_choose = False
            self.keyview_cell.stop_editing(True)
            return

        # If the widget that has focus is an entry widget, do not use the keybindings
        if type(widget.get_focus()) == gtk.Entry:
            return

        # check the masks and then try and run the function supplied
        combo = ""
        if event.state & gtk.gdk.CONTROL_MASK:
            combo += "ctrl_"
        if event.state & gtk.gdk.MOD1_MASK:
            combo += "alt_"
        if event.state & gtk.gdk.SHIFT_MASK:
            combo += "shift_"
        combo += keyname
        try:
            func = getattr(self, keybindings[combo], None)
        except KeyError:
            func = None

        if func:
            func(None)
            return True

# SIGNAL HANDLERS

    def play_event(self, obj):
        if not self.pympdStatus: return
        stateStr = self.pympdStatus.state
        if stateStr == 'play':
            self.pympd.pause()
            self.playIcon.set_from_stock(gtk.STOCK_MEDIA_PLAY, gtk.ICON_SIZE_LARGE_TOOLBAR)
        elif stateStr == 'pause' or 'stop':
            self.pympd.play()
            self.updateLabels(True)
            self.playIcon.set_from_stock(gtk.STOCK_MEDIA_PAUSE, gtk.ICON_SIZE_LARGE_TOOLBAR)

    def prev_event(self, obj):
        self.pympd.prev()
        return

    def next_event(self, obj):
        self.pympd.next()
        return

    def expand_browser_event(self, obj):
        if self.browserbox.get_property('visible'):
            self.browserbox.hide()
            self.browserExpander.set_label("Show Browser")
        else:
            self.browserbox.show()
            self.browserExpander.set_label("Hide Browser")
        return

    def compact_event(self, obj):
        if self.playlistBox.get_parent() == self.playlistParent:
            if not get_bool('decorated'):
                self.mainWindow.set_decorated(False)
            set_bool('compact', True)
            self.saveWindowSize()
            self.mainWindow.resize(1, 1)
            self.statsLabel.hide()
#            self.playlistBox.hide()
            #self.linkBox.hide()
            #self.titleLabel.hide()


            # Save visibilities and show everything while in compact mode.
            self.visibility = [self.hdBox.get_property('visible'), self.browserVBox.get_property('visible'), self.playlistBox.get_property('visible')]
            self.hdBox.show()
            self.playlistBox.show()

            # set button relief to gtk.RELIEF_NONE.
            for child in self.controlBox.get_children():
                child.set_relief(gtk.RELIEF_NONE)



            #Hide a bunch of stuff to go into compact mode.
            self.titleVBox.hide()
            self.statusBar.hide()
            self.sliderLabel.hide()
#            self.slider.reparent(self.leftControlBox)
            self.topHSeparator.hide()
            self.topBox.hide()
            self.extendedParent.hide()

            if get_bool('playlist_open'):
                self.playlistWindow.show()
            if get_bool('plugins_open'):
                self.pluginWindow.show()
            
            self.leftControlBox.set_child_packing(self.marqueeParent, True, True, 0, gtk.PACK_START)
            self.marqueeEntry.show()
            self.marqueeEntry.set_scroll(True)
            self.playlistBox.reparent(self.externPlaylist)
#            self.notebook.reparent(self.pluginWindow)
            self.hdBox.reparent(self.externBrowser)
            self.compact = True
            self.mainWindow.set_property('resizable',False)
            self.xml.get_widget("view_browser").set_property('sensitive', True)
            self.xml.get_widget("view_plugins").set_property('sensitive', True)

            # It's weird, but if someone compacts with volume open, let's follow the window.
            if self.volumeWindow.get_property('visible'):
                self.volume_slider_display(None, True)
        else:
            set_bool('compact', False)
            self.mainWindow.set_decorated(True)
            self.playlistWindow.hide()
            self.pluginWindow.hide()
            self.browserWindow.hide()

            self.hdBox.reparent(self.playlistParent)
            self.playlistBox.reparent(self.playlistParent)
            self.hdBox.reparent(self.browserParent)
#            self.notebook.reparent(self.extendedParent)

            # Change the relief to full (normal)
            for child in self.controlBox.get_children():
                child.set_relief(gtk.RELIEF_NORMAL)

            # Restore the visibility as we left it before.
            self.hdBox.set_property('visible', self.visibility[0])
            self.browserVBox.set_property('visible', self.visibility[1])
            self.playlistBox.set_property('visible', self.visibility[2])

            self.mainWindow.resize(int(get('width')), int(get('height')))
            self.mainWindow.set_property('resizable',True)
            self.marqueeEntry.hide()
            self.marqueeEntry.set_scroll(False)
            self.leftControlBox.set_child_packing(self.marqueeParent, False, True, 0, gtk.PACK_START)
            self.extendedParent.show()
            self.extendedParent.queue_draw()
            self.mainWindow.queue_draw()
            self.sliderLabel.show()
#            self.slider.reparent(self.sliderBox)
            self.topHSeparator.show()
            self.topBox.show()
            #self.titleLabel.show()
            #self.linkBox.show()
            self.titleVBox.show()
            self.statsLabel.show()
            self.playlistParent.show()
            if self.statusbarVis:
                self.statusBar.show()
            self.compact = False
            self.xml.get_widget("view_browser").set_property('sensitive', False)
            self.xml.get_widget("view_plugins").set_property('sensitive', False)

            # Reset self.view
            self.view = self.source

            # If the volume window is open, redisplay it.
            if self.volumeWindow.get_property('visible'):
                self.volume_slider_display(None, True)

            self.updateLabels(True)

    def libraryViewChanged(self, obj):
        selection_text = self.libraryViewCombobox.get_active_text()
        if selection_text == "Filesystem":
            self.view = HARDDRIVE
            self.browserVBox.hide()
            self.hdBox.show()
            if self.connected and not self.hdStoreBuilt:
                self.updateHDStore()
        elif selection_text == "ID3 Tags":
            self.view = DATABASE
            self.hdBox.hide()
            self.browserVBox.show()
        return

    def browser_clicked(self, obj, event):
        self.focused = obj
        self.browserSelection = obj.get_selection()
        #if self.menu_opened:
        #    self.browser_menu.popdown()
            #self.menu_opened = False
        #    return False
        if event.button == 3:
            path = obj.get_path_at_pos(int(event.x),int(event.y))
            selection = obj.get_selection()
            rows = selection.get_selected_rows()
            if path[0] not in rows[1]:
                selection.unselect_all()
                selection.select_path(path[0])
            self.browser_menu.popup(None, None, None, event.button, event.time)
            #self.menu_opened = True
            return True
        if event.button == 3:
            return True
        return False

    def browser_update(self, obj):
        self.update_event(obj)
        return

    def browser_add(self, obj):
        if self.browserSelection == self.artistSelection:
            self.trackSelection.select_all()
            self.append_tracks(None)
            return
        if self.browserSelection == self.albumSelection:
            self.trackSelection.select_all()
            self.append_tracks(None)
            return
        if self.browserSelection == self.trackSelection:
            self.append_tracks(None)
            return
        if self.browserSelection == self.hdSelection:
            self.append_tracks(None)
            return
        else:
            print "Weeeeeeee. This shouldn't be happening. check browser_add"

    def browser_replace(self, obj):
        if self.browserSelection == self.artistSelection:
            self.trackSelection.select_all()
            self.replace_tracks(None)
            return
        if self.browserSelection == self.albumSelection:
            self.trackSelection.select_all()
            self.replace_tracks(None)
            return
        if self.browserSelection == self.trackSelection:
            self.replace_tracks(None)
            return
        if self.browserSelection == self.hdSelection:
            self.replace_tracks(None)
            return
        else:
            print "Weeeeeeee. This shouldn't be happening. check browser_replace"

    def playlist_clicked(self, obj, event):
        #if self.menu_opened:
        #    self.playlist_menu.popdown()
        #    self.menu_opened = False
        #    print "Closing menu"
        #    return False
        if event.button == 3:
            path = obj.get_path_at_pos(int(event.x),int(event.y))
            selection = obj.get_selection()
            rows = selection.get_selected_rows()
            if path[0] not in rows[1]:
                selection.unselect_all()
                selection.select_path(path[0])
            self.playlist_menu.popup(None, None, None, event.button, event.time)
            #self.menu_opened = True
            return True
        #if event.button == 3:
        #    return True
        return False

    def playlist_chooser_clicked(self, obj, event):
        if event.button == 3:
            path = obj.get_path_at_pos(int(event.x),int(event.y))
            selection = obj.get_selection()
            rows = selection.get_selected_rows()
            try:
                if path[0] not in rows[1]:
                    selection.unselect_all()
                    selection.select_path(path[0])
                # user right click on playlist, so make sure the menu
                # items are sensitive
                self.playlistChooserEnqueue.set_property('visible',True)
                self.playlistChooserPlay.set_property('visible',True)
                self.playlistChooserDelete.set_property('visible',True)
                self.playlistChooserSeparator.set_property('visible',True)
            except TypeError: # user right clicked, but not on a playlist
                self.playlistChooserEnqueue.set_property('visible',False)
                self.playlistChooserPlay.set_property('visible',False)
                self.playlistChooserDelete.set_property('visible',False)
                self.playlistChooserSeparator.set_property('visible',False)
                pass
            self.playlist_chooser_menu.popup(None, None, None, event.button, event.time)
            return True
        return False

    def playlist_remove(self, obj):
        self.songIDs = []
        self.songSelection.selected_foreach(self.foreach_song_selected, None)
        self.pympd.deleteid(tuple(self.songIDs))
        return

    def playlist_crop(self, obj):
        pass

    def playlist_clear(self, obj):
        self.pympd.clear()
        return


# menubar handlers

# TODO: create a function to generate this signal.

    def toggle_view_plugins(self, obj):
        self.pluginDialog.show()

    def toggle_view_browser(self, obj):
        self.xml.get_widget("view_browser").activate()

    def change_view_plugins(self, obj):
        if obj.get_active():
            self.pluginWindow.show()
        else:
            self.pluginWindow.hide()
        set_bool('plugins_open', obj.get_active())

    def change_view_browser(self, obj):
        if obj.get_active():
            self.playlistWindow.show()
        else:
            self.playlistWindow.hide()
        set_bool('playlist_open', obj.get_active())

    def change_view_sidebar(self, obj):
        if obj.get_active():
            self.sideBar.show()
        else:
            self.sideBar.hide()
    
    def change_view_menubar(self, obj):
        if obj.get_active():
            self.menuBar.show()
        else:
            self.menuBar.hide()

    def change_view_statusbar(self, obj):
        if obj.get_active():
            self.statusbarVis = True
            self.statusBar.show()
        else:
            self.statusbarVis = False
            self.statusBar.hide()

    def toggle_view_sidebar(self, obj):
        self.sideBarToggle.set_active(not self.sideBarToggle.get_active())

    def toggle_view_menubar(self, obj):
        self.menuBarToggle.set_active(not self.menuBarToggle.get_active())

    def playlist_jump_to_song(self, obj=None):
        iter = self.songSort.get_iter_first()
        model = self.songView.get_model()
        try:
            while iter:
                if self.songID == model.get_value(iter, 6):
                    self.songView.scroll_to_cell(self.songSort.get_path(iter), None, True, 0.5, 0)
                    #TODO: remove hardcoded path to PLAYLIST.
                    self.sourceSelection.select_path((1, ))
                    return
                iter = model.iter_next(iter)
        except Exception, e:
            debug("%s %s" % (Exception, e))
        return

    def playlist_info(self, obj):
        paths = self.songSelection.get_selected_rows()[1]
        songs = []
        self.info = []
        for path in paths:
            iter = self.songSort.get_iter(path)
            songID = self.songSort.get_value(iter, 6)
            songs.extend([self.pympd.listSongInPlaylist(songID)])
        if songs == []:
            self.info.extend([self.getExtSongInfo()])
        for song in songs:
            self.info.extend([self.getExtSongInfo(song)])
        self.infoIndex = 0
        self.showInfo(self.infoIndex)
        self.infoDialog.present()

    def showInfo(self, index):
        # If nothing is selected, we return.
        if len(self.info) <= 0:
            return
        info = self.info[index]
        self.infoFile.set_text(info[0].strip())
        self.infoArtist.set_text(info[1].strip())
        self.infoTitle.set_text(info[2].strip())
        self.infoAlbum.set_text(info[3].strip())
        self.infoTrack.set_text(info[4].strip())
        self.infoLength.set_text(secToString(int(info[5])))
        if index > 0 and index < len(self.info) - 1:
            self.infoPrev.set_property('sensitive', True)
            self.infoNext.set_property('sensitive', True)
        elif index < len(self.info) - 1:
            self.infoNext.set_property('sensitive', True)
            self.infoPrev.set_property('sensitive', False)
        elif index > 0:
            self.infoNext.set_property('sensitive', False)
            self.infoPrev.set_property('sensitive', True)
        else:
            self.infoNext.set_property('sensitive', False)
            self.infoPrev.set_property('sensitive', False)
        return

    def info_prev(self, obj):
        self.infoIndex = self.infoIndex - 1
        self.showInfo(self.infoIndex)

    def info_next(self, obj):
        self.infoIndex = self.infoIndex + 1
        self.showInfo(self.infoIndex)

    def info_close(self, obj):
        self.infoDialog.hide()

    def playlist_add(self, obj):
        if self.connected and not self.hdStoreBuilt:
            self.updateHDStore()
        self.browserWindow.present()
        self.view = HARDDRIVE

    def playlist_close(self, obj):
        self.playlistWindow.hide()

    def playlistDialogCancel(self, index):
        self.playlistSaveDialog.hide()
        return

    def playlistDialogSave(self, obj):
        name = self.playlistSaveEntry.get_text()
        try:
#            self.pympd.rm(name)
            self.pympd.save(name)
        except:
            print "Error Saving Playlist"

        self.playlistSaveDialog.hide()
        self.playlistSaveEntry.set_text("")
        self.updatePlaylistStore()
        return

    def update_prefs(self):
        return

    def prefs_activate(self, obj):
        self.prefsDialog.show()
        return

    def prefs_close(self, obj):
        set('host', self.prefConnHostEntry.get_text())
        set('port', self.prefConnPortEntry.get_text())
        self.saveConfigParser(CONFIG_FILE)
        self.prefsDialog.hide()

    def save_playlist(self, obj, suffix=None):
        self.playlistSaveDialog.present()

    def del_playlist(self, obj):
        model, iter = self.playlistSelection.get_selected()
        if iter == None:
            return
        playlist = model.get_value(iter, 1)
        self.deletePlaylist(playlist)
        return

    def append_playlist(self, obj):
        model, iter = self.playlistSelection.get_selected()
        if not iter == None:
            playlist = model.get_value(iter, 1)
            self.appendPlaylist(playlist)
        return

    def replace_playlist(self, obj):
        model, iter = self.playlistSelection.get_selected()
        if not iter == None:
            playlist = model.get_value(iter, 1)
            self.replacePlaylist(playlist)
        self.pympd.play()
        return

    def append_tracks(self, obj):
        self.songs = []
        self.append()
        return

    def replace_tracks(self, obj):
        self.songs = []
        self.replace()
        return

    def update_event(self, obj):
        self.updateDBButton.set_property('sensitive', False)
        try:
            self.updating = self.pympd.update()
        except:
            print "Problem updating DB"

    def showmenu_toggled(self, obj):
        self.menuBar.set_property('visible', not self.menuBar.get_property('visible'))


    def shuffle_clicked(self, obj, event=None):
        self.pympd.random()
        return False

    def repeat_clicked(self, obj, event=None):
        self.pympd.repeat()
        return False

    def crossfade_clicked(self, obj, event=None):
        if not obj.get_active():
            set('crossfade', self.prefServerXFadeSpinner.get_value_as_int())
        else:
            set('crossfade', 0)
        self.pympd.crossfade(get('crossfade'))
        return False

    def set_xfade(self, obj):
        if self.setting_xfade:
            return True
        self.pympd.crossfade(obj.get_value_as_int())
#        set('crossfade', obj.get_value_as_int())
        return True

    def slider_moved(self, obj, data):
        self.sliding = True

    def slider_changed(self, obj, data):
        new_position = int(obj.get_adjustment().get_value())
        diff = self.elapsedTime - new_position
        if not (diff > -4 and diff < 4):
            self.pympd.seek(self.pympdStatus.song, new_position)
            self.pympdStatus = self.pympd.status()
            self.updateUI(self.pympdStatus)
        self.sliding = False
        return

    def disconnect_mpd(self, obj):
        #DONE: actually perform the disconnect
        self.pympd._disconnect()
        return

    def connect_mpd(self, obj):
        #DONE: actually perform the connect, check if self.prefConnAuthCheckbox
        # is active too, to send the password along with the connection
        self.initMpd()
        self.correctConnectionButtons()
        return

    def useAuth_toggled(self, obj):
        if self.prefConnAuthCheckbox.get_active():
            self.prefConnPassEntry.set_property('sensitive',True)
        else:
            self.prefConnPassEntry.set_property('sensitive',False)
	set_bool('use_auth', not get_bool('use_auth'))

    def conn_auto_toggled(self, obj):
        #DONE: setup auto connection stuff
        set_bool('autoconnect', not get_bool('autoconnect'))
        gobject.timeout_add(int(get('timeout'))*1000, self.autoConnect)
        return


    def enqueue_toggled(self, obj):
        if self.prefClientDblClickEnqueue.get_active():
            set('double_click', "enqueue")
        else:
            set('double_click', "replace")

    def dbALL_toggled(self, obj):
        set_bool('db_all', not get_bool('db_all'))

    def stopQuit_toggled(self, obj):
        set_bool('stop_quit', not get_bool('stop_quit'))

    def debug_toggled(self, obj):
        set_bool('debug', not get_bool('debug'))

    def filterpl_toggled(self, obj):
        set_bool('filter_database', not get_bool('filter_database'))
        
    def filterdb_toggled(self, obj):
        set_bool('filter_playlist', not get_bool('filter_playlist'))

    def followsong_toggled(self, obj):
        set_bool('followsong', not get_bool('followsong'))

    def sticky_toggled(self, obj):
        set_bool('sticky', not get_bool('sticky'))
        if get_bool('sticky'):
            self.mainWindow.stick()
        else:
            self.mainWindow.unstick()

    def ontop_toggled(self, obj):
        set_bool('alwaysontop', not get_bool('alwaysontop'))
        self.mainWindow.set_keep_above(get_bool('alwaysontop'))

    def decor_toggled(self, obj):
        set_bool('decorated', not get_bool('decorated'))
        if self.compact:
            self.mainWindow.set_decorated(get_bool('decorated'))

    def show_about(self, obj):
        self.aboutDialog.set_property('visible',True)
    
    def hide_about(self, obj):
        print "YOYO"
        self.aboutDialog.set_property('visible',False)

    def key_remove_event(self, obj):
        model, iter = self.keySelection.get_selected()
        if iter == None:
            return
        model.remove(iter)

    def key_add_event(self, obj):
        self.keyStore.append((False, False, False, 'NEW', ''))

    def key_save_event(self, obj):
        keybindings.clear()
        model = self.keyView.get_model()
        iter = model.get_iter_first()
        while iter:
            row = model.get(iter, 0, 1, 2, 3, 4) # get all items in the row.
            ctrl, alt, shift, key, action = row
            binding = ""
            if ctrl:
                binding += 'ctrl_'
            if alt:
                binding += 'alt_'
            if shift:
                binding += 'shift_'
            binding += key
            keybindings[binding] = actions[action]
            iter = model.iter_next(iter)

    def configure_event(self, obj, event):
        if self.volumeWindow.get_property("visible"):
            self.volume_slider_display(None, True)


    def quit_event(self, obj, data=None):
        if not self.compact:
            self.saveWindowSize()
        #saves the list of running plugins and unloads them.
        self.savePlugins()
        self.saveVisColumns()
        self.saveConfigParser(CONFIG_FILE)
        if get_bool('stop_quit'):
            self.pympd.pause()
        gtk.main_quit()

    def destroy_decoy(self, obj, data=None):
        if obj == self.playlistWindow:
            set_bool('playlist_open', False)
            self.xml.get_widget("view_browser").set_property('active', False)
        if obj == self.pluginWindow:
            set_bool('plugins_open', False)
            self.xml.get_widget("view_plugins").set_property('active', False)

        obj.hide()
        return True

# CALLBACKS


    # For the directory browser
    def foreach_dir_selected(self, model, path, iter, data):
        pass

    # For the database browser
    def foreach_artist_selected(self, model, path, iter, data):
        if self.artistSelection.iter_is_selected(iter):
            artist = [replaceAmp(model.get_value(iter, 0))]
            self.artists.extend(artist)
        return

    def foreach_album_selected(self, model, path, iter, data):
        if self.albumSelection.iter_is_selected(iter):
            album = [replaceAmp(model.get_value(iter, 0))]
            self.albums.extend(album)
        return

    def foreach_track_selected(self, model, path, iter, data):
        if self.trackSelection.iter_is_selected(iter):
            song = model.get_value(iter, 4)
            self.songs.append(song)
        return

    def foreach_file_selected(self, model, path, iter, data):
        if self.hdSelection.iter_is_selected(iter):
            song = model.get_value(iter, 2)
            self.songs.append(song)
        return

    def foreach_song_selected(self, model, path, iter, data):
        if self.songSelection.iter_is_selected(iter):
            songID = int(model.get_value(iter, 6))
            self.songIDs.extend([songID])
        return


    def dir_expanded(self, treeview, iter, path):
        store = treeview.get_model()
        child = store.iter_children(iter)
        iter = store.get_iter(path)
        if store.get_value(child, 0) is None:
            store.remove(child)
            dir = store.get_value(iter, 2)
            readDir(iter, dir, store, self.pympd)
        treeview.expand_row(path, False)

    def artist_selected(self, path, args):
        if self.artistSelected:
            self.artistSelected = False
            self.artistSelection.selected_foreach(self.foreach_artist_selected, None)
        else:
            if self.artistSelection.path_is_selected(path):
                return True
            iter = self.artistStore.get_iter(path)
            self.artists = [replaceAmp(self.artistStore.get_value(iter, 0))]
            self.artistSelected = True
            self.artistSelection.select_path(path)
            return True
        if ALL in self.artists:
            self.artists.remove(ALL)
            self.updateAlbumListStore()
            self.updateTrackListStore()
        else:
            self.updateAlbumListStore(self.artists)
            self.updateTrackListStore(self.artists, None)
        return


    def album_selected(self, path, args):
        if self.albumSelected:
            self.albumSelected = False
            self.albumSelection.selected_foreach(self.foreach_album_selected, None)
        else:
            if self.albumSelection.path_is_selected(path):
                return True
            iter = self.albumStore.get_iter(path)
            self.albums = [replaceAmp(self.albumStore.get_value(iter, 0))]
            self.albumSelected = True
            self.albumSelection.select_path(path)
            return True
        if ALL in self.albums:
            self.albums.remove(ALL)
            self.updateTrackListStore(self.artists, None)
        else:
            self.updateTrackListStore(self.artists, self.albums)
        return

    def source_selected(self, path, args):
        iter = self.sourceStore.get_iter(path)
        source = self.sourceStore.get_value(iter, 1)
        if not source == self.source:
            self.source = source
            
            def only_show(showme):
                items = [self.browserVBox, self.playlistBox, self.hdBox, self.libraryBox]
                items.remove(showme)
                for item in items:
                    item.hide()
                showme.show()
            # so we don't keep stopping the playlist when we change move to PLAYLIST
            if source == PLAYLIST:
                only_show(self.playlistBox)
                #self.browserVBox.hide()
                #self.hdBox.hide()
                #self.playlistBox.show()
            else:
                self.view = source
                if source == DATABASE:
                    if not self.sourceOnce:
                        self.sourceOnce = True
                        config['db_all'] = TRUE
                        if get_bool('db_all') and not self.alltrackStoreBuilt:
                            self.artistSelection.select_path((0, ))
                    only_show(self.browserVBox)
                    #self.browserVBox.show()
                    #self.playlistBox.hide()
                    #self.hdBox.hide()
                    return True

                elif source == HARDDRIVE:
                    only_show(self.hdBox)
                    #self.browserVBox.hide()
                    #self.playlistBox.hide()
                    #self.hdBox.show()
                    if self.connected and not self.hdStoreBuilt:
                        self.updateHDStore()
#                    profile(self.updateHDStore)
                elif source == LIBRARY:
                    only_show(self.libraryBox)
                    self.browserVBox.reparent(self.libraryBox)
                    self.hdBox.reparent(self.libraryBox)
                    self.browserVBox.show()
                    self.hdBox.show()
                    self.libraryViewChanged(None)
#        if self.connected:
#            self.updateSongStore()
        return True

    def playlist_selected(self, path, args):
        #TODO: we want to be able to read playlists when they aren't loaded in mpd. (expected in 0.12)
        return True
#        iter = self.playlistStore.get_iter(path)
#        if self.playlistSelection.path_is_selected(path):
#            playlist = self.playlistStore.get_value(iter, 0)
#            if not playlist == self.playlist:
#                if playlist == "Current":
#                    self.updateSongStore()
#                else:
#                    self.updateSongStore(playlist)
#                print "Updating"
#        return True

    def plugin_selected(self, path, args):
        model = self.pluginView.get_model()
        plugin = model[path][0]
        if not self.pluginSelection.path_is_selected(path):
            self.plugin_buttons(plugin)
        if not self.pluginSelection.path_is_selected(path):
            self.plugin_info(plugin)
        return True

    # different from the other _selected functions because it is a call back on 'row-activated'
    def song_activated(self, treeview, path, column):
        model = treeview.get_model()
        iter = model.get_iter(path)
        songID = int(model.get_value(iter, 6))
        self.pympd.play(songID)
        self.updateLabels(True)
        return

    def file_activated(self, treeview, path, column):
        model = treeview.get_model()
        iter = model.get_iter(path)
        if model.get_value(iter, 0) == 'gtk-directory':
            if treeview.row_expanded(path):
                treeview.collapse_row(path)
            else:
                treeview.expand_row(path, False)


    def artist_activated(self, treeview, path, column):
        return
        self.trackSelection.select_all()
        if get('double_click') == "enqueue":
            self.append_tracks(None)
        else:
            self.replace_tracks(None)
        return

    def album_activated(self, treeview, path, column):
        return
        self.trackSelection.select_all()
        if get('double_click') == "enqueue":
            self.append_tracks(None)
        else:
            self.replace_tracks(None)
        return

    def track_activated(self, treeview, path, column):
        if get('double_click') == "enqueue":
            self.append_tracks(None)
        else:
            self.replace_tracks(None)
        return



    # 'edited' callback for our source list.
    def playlist_edited(self, cell, path, new_text, data):
        return
        #TODO: we want to be able to move playlists without loading them in mpd.
#        old_playlist = cell.get_property('text')
#        new_playlist = new_text
#        if old_playlist == new_playlist:
#            return
#        self.pympd.rm(old_playlist)
#        self.pympd.save(new_playlist)
#        print old_playlist, "saved as", new_playlist
#        self.updatePlaylistStore()
#        return

    def plugin_cb(self, columns, cell, model, iter):
        if self.plugins.has_key(model.get_value(iter, 0)):
            cell.set_property('active', True)
        else:
            cell.set_property('active', False)
        return

    def plugin_togglecb(self, cell, path, user_data):
        model = self.pluginView.get_model()
        iter = model.get_iter(path)
        plugin = model.get_value(iter, 0)
        if self.plugins.has_key(plugin):
            self.unloadPlugin(plugin)
        else:
            self.loadPlugin(plugin)
        return

    # TODO: look into this to see if it is the right way of doing it.
    def keyview_togglecb(self, cell, path, col):
        model = self.keyView.get_model()
        iter = model.get_iter(path)
        model.set(iter, col, not cell.get_active())
        cell.set_active(True)
        return

# def callback(cellrenderer, editable, path, user_param1, ...)
    def keyview_key_edited(self, cell, editable, path, data):
        self.key_choose = True
        model = self.keyView.get_model()
        self.keyview_iter = model.get_iter(path)
        self.keyview_cell = cell
        gtk.gdk.keyboard_grab(self.mainWindow.window, 0, 0)
#        model = self.keyView.get_model()
#        iter = model.get_iter(path)
#        model.set(iter, 3, new_text)
        return

    def keyview_action_edited(self, cell, path, new_text, data):
        model = self.keyView.get_model()
        iter = model.get_iter(path)
        model.set(iter, 4, new_text)
        return

    def playview_togglecb(self, cell, path, user_data):
        model = self.playlistPrefView.get_model()
        iter = model.get_iter(path)
        column = model.get_value(iter, 2)
        self.visColumns[column] = not self.visColumns[column]
        column.set_visible(self.visColumns[column])
        return True

    def playview_cb(self, columns, cell, model, iter):
        if self.visColumns[model.get_value(iter, 2)]:
            cell.set_property('active', True)
        else:
            cell.set_property('active', False)
        return

    def alpha_sort(self, model, iter1, iter2, data):
        value1 = model.get_value(iter1, 0).lower().strip()
        value2 = model.get_value(iter2, 0).lower().strip()
        if value1 == ALL.lower():
            return FALSE
        elif value2 == ALL.lower():
            return TRUE
        elif value1 == value2:
            return EQUAL
        elif value2 > value1:
            return FALSE
        else:
            return TRUE

    def focus_filter(self, arg=None):
        if self.playlistBox.get_property("visible"):
            self.searchSongEntry.grab_focus()
        if self.browserVBox.get_property("visible"):
            self.searchTrackEntry.grab_focus()

    def loadModule(self, module):
        if not module in self.modules:
            module_info = imp.find_module(module, [self.dir.plugin])
            ret_module = imp.load_module(module, *module_info)
            self.modules[module] = ret_module
        return self.modules[module]


    # send a bunch of stuff to plugins for them to use, will have to rewrite all the plugins now, though. :-/
    # TODO
    def loadPlugin(self, plugin):
        try:
            gety = self.__getattribute__
            data = [self.xml, self.pluginMenu, self.pympd, self.dir.base, self.parser]
            module = self.loadModule(plugin)
            try:
                cl = getattr(module, '__class') # See if the plugin defines its own class entrypoint.
                instance = getattr(module, cl)()
            except:
                instance = getattr(module, plugin.capitalize())()
            instance._init(data, self.__getattribute__)
#            _import = "import %s" % plugin
#            _instance = "instance = %s.%s()" % (plugin, plugin.capitalize())
#            exec _import
#            exec _instance
#            instance._init(data)
            debug("%s plugin loaded" % (plugin), False)
        except Exception, e:
            debug("Error importing %s" % (plugin), False)
            debug(str(e))
            return False
        self.plugins[plugin] = instance
        self.plugin_buttons(plugin)
        return True

    def unloadPlugin(self, plugin):
        try:
            self.plugins[plugin]._unload()
            sys.modules.pop(plugin)
            del self.plugins[plugin]
            del self.modules[plugin]
            debug("%s plugin unloaded" % plugin, False)
            self.plugin_buttons(plugin)
            return True
        except Exception, e:
            print "Error unloading %s" % plugin
            debug(str(e))
            self.plugin_buttons(plugin)
            return False

    def spinPlugins(self, data, songChanged=False):
        for plugin in self.plugins.keys():
            try:
                self.plugins[plugin]._spin(data, songChanged)
            except Exception, e:
                debug("%s Error: %s" % (plugin, str(e)))
