EventGhost/EventGhost

View on GitHub
plugins/MediaMonkey/__init__.py

Summary

Maintainability
F
1 mo
Test Coverage
# -*- coding: utf-8 -*-
#
# plugins/MediaMonkey/__init__.py
#
# Copyright (C)  2009-2011 Pako  <lubos.ruckl@quick.cz>
#
# This file is a plugin for EventGhost.
# Copyright © 2005-2020 EventGhost Project <http://www.eventghost.net/>
#
# EventGhost is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 2 of the License, or (at your option)
# any later version.
#
# EventGhost is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along
# with EventGhost. If not, see <http://www.gnu.org/licenses/>.
#
# Changelog (in reverse chronological order):
# -------------------------------------------
# 0.3.7 by Pako 2014-06-08 12:06 UTC+1
#     - - changes caused by a new eg.Scheduler
# 0.3.6 by Pako 2013-02-13 08:38 UTC+1
#     - bugfix (typing error)
# 0.3.5 by Pako 2013-02-11 19:37 UTC+1
#     - on Windows 7, FindWindowEx returns None without raising an error
#     - on older versions, it raises a FILE_NOT_FOUND error
# 0.3.4 by Pako 2012-08-17 07:51 UTC+1
#     - added MediaMonkey.IsRunning event
#     - added GetPlaylists action
#     - improved routine for periodic testing "is running ?"
# 0.3.3 by Pako 2012-01-06 15:30 UTC+1
#     - CoverArt support added
# 0.3.2 by Pako 2012-01-06 15:30 UTC+1
#     - action Exit renamed
# 0.3.1 by Pako 2011-12-24 10:05 UTC+1
#     - plugin automatically connects and disconnects to MM through ActiveX/COM
# 0.3.0 by Pako 2011-11-28 07:12 UTC+1
#     - for MM version 4.0 and later
#     - removed file EventGhost.vbs
# 0.2.12 by Pako 2010-02-10 12:24  GMT+1
#     - all previous changes
#===============================================================================

import wx
import time
import datetime
import wx.lib.masked as masked
import codecs
from win32gui import MessageBox, FindWindow
from win32process import GetWindowThreadProcessId
from win32com.client import Dispatch, DispatchWithEvents
from eg.WinApi import SendMessageTimeout
from eg.WinApi.Utils import CloseHwnd
from os.path import split as path_split, isfile, abspath, join, dirname
from os import remove as remove_file
from functools import partial
from random import randint
from _winreg import OpenKey, HKEY_LOCAL_MACHINE, QueryValueEx, CloseKey
from ctypes import windll, c_buffer, c_ulong, byref, sizeof

_psapi = windll.psapi
_kernel = windll.kernel32
modBasName = c_buffer(30)
hModule = c_ulong()
clength = c_ulong()
GW_HWNDNEXT = 2
GW_CHILD = 5
PROCESS_QUERY_INFORMATION = 0x0400
PROCESS_VM_READ = 0x0010
WM_SYSCOMMAND = 274
SC_MINIMIZE   = 61472
#====================================================================

eg.RegisterPlugin(
    name = "MediaMonkey",
    author = "Pako",
    version = "0.3.7",
    kind = "program",
    guid = "{50602341-ABC3-47AD-B859-FCB8C03ED4EF}",
    createMacrosOnAdd = True,
    description = ur'''<rst>
Adds support functions to control MediaMonkey_.

This plugin requires MediaMonkey_ version 4.0 or later.

| **Note:**
| This new version no longer needs for its work the file "EventGhost.vbs" !!!
| Please remove this file (if installed) and restart MediaMonkey (if running).
| Then restart EventGhost !

| **Plugin's events:**
| If you select one (or more) of these options, some actions will trigger
  an event upon completion and a payload is used to indicate the results of the action.

| The event triggered will be constructed as follows:
| *Prefix:* MediaMonkey
| *Suffix:* a string indicating the particular command (e.g. playlist, or track_added)
| *Payload:* this will depend on the results of the action

For example, if you try to add a track to a playlist 'test', the payload will be either:
*u"Track already exists in playlist test"* or *u"Track added to playlist test"*.

| Another example: executing 'Load Playlist by Filter', where you choose name
  the filter 'Beatles', the result may be: MediaMonkey.playlist (u'Beatles','220') .
| Number 220 indicates, that there are 220 tracks in the library that match your query.

.. _MediaMonkey: http://www.MediaMonkey.com/ ''',
    url = "http://www.eventghost.net/forum/viewtopic.php?t=563",
    icon = (
        "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"
        "c1G/AAAAAElFTkSuQmCC"
    ),
)
#===============================================================================

MM_EVENTS = (
"OnPlay",
"OnPause",
"OnStop",
"OnSeek",
"OnTrackEnd",
"OnPlaybackEnd",
"OnCompletePlaybackEnd",
"OnTrackSkipped",
"OnShutdown",
"OnRepeatClicked",
"OnShuffleClicked",
"OnFilterChange",
"OnOptionsChange",
"OnChangedSelection",
"OnNowPlayingModified",
"OnNowPlayingSelectionChanged",
"OnBeforeTracksMove",
"OnTrackAdded",
"OnTrackConverted",
"OnTrackDeleting",
"OnTrackListFilled",
"OnTrackListFilling",
"OnTrackListModified",
"OnTrackListSelectionChanged",
"OnTrackProperties",
"OnDownloadFinished",
"OnIdle",
)

SONG_TABLE_FIELDS=(
    ("Album","T","AlbumName"),
    ("AlbumArtist","T","AlbumArtistName"),
    ("Artist","T","ArtistName"),
    ("AudioCDTrack","I",""),
    ("Author","T","Author"),
    ("Band","T","Band"),
    ("Bitrate","I","Bitrate"),
    ("BPM","I","BPM"),
    ("Broadcast","I",""),
    ("CacheName","T","CachedPath"),
    ("CacheStatus","I","Cached"),
    ("Comment","T","Comment"),
    ("Conductor","T","Conductor"),
    ("Copyright","T","Copyright"),
    ("Copyrighted","I",""),
    ("Custom1","T","Custom1"),
    ("Custom2","T","Custom2"),
    ("Custom3","T","Custom3"),
    ("Custom4","T","Custom4"),
    ("Custom5","T","Custom5"),
    ("DateAdded","D","DateAdded"),
    ("DiscNumber","T","DiscNumberStr"),
    ("EncodedBy","T",""),
    ("Encoder","T","Encoder"),
    ("FileLength","I","FileLength"),
    ("FileModified","D","FileModified"),
    ("GaplessBytes","I","GaplessBytes"),
    ("Genre","T","Genre"),
    ("GroupDesc","T","Grouping"),
    ("ID","I","ID"),
    ("IDAlbum","I",""),
    ("IDFolder","I",""),
    ("IDMedia","I","Media"),
    ("InitialKey","T",""),
    ("InvolvedPeople","T","InvolvedPeople"),
    ("ISRC","T","ISRC"),
    ("Language","T",""),
    ("LastTimePlayed","D","LastPlayed"),
    ("Lyricist","T","Lyricist"),
    ("Lyrics","T","Lyrics"),
    ("MaxSample","R",""),
    ("MediaType","T",""),
    ("Mood","T","Mood"),
    ("NormalizeAlbum","R","LevelingAlbum"),
    ("NormalizeTrack","R","Leveling"),
    ("Occasion","T","Occasion"),
    ("OrigArtist","T","OriginalArtist"),
    ("OrigFileLength","I",""),
    ("Original","I",""),
    ("OrigLyricist","T","OriginalLyricist"),
    ("OrigTitle","T","OriginalTitle"),
    ("OrigYear","I","OriginalYear"),
    ("PlaybackPos","I","Bookmark"),
    ("PlayCounter","I","PlayCounter"),
    ("PostGap","I","PostGap"),
    ("PreGap","I","PreGap"),
    ("PreviewLength","I",""),
    ("PreviewName","T","PreviewPath"),
    ("PreviewStartTime","I",""),
    ("PreviewState","I",""),
    ("Publisher","T","Publisher"),
    ("Quality","T","Quality"),
    ("Rating","I","Rating"),
    ("RatingString","T",""),
    ("Remixer","T",""),
    ("SamplingFrequency","I","SampleRate"),
    ("Seekable","I","isBookmarkable"),
    ("SignPart1","I",""),
    ("SignPart2","I",""),
    ("SignPart3","I",""),
    ("SignPart4","I",""),
    ("SignType","I",""),
    ("SongLength","I","SongLength"),
    ("SongPath","T","Path"),
    ("SongTitle","T","Title"),
    ("Stereo","I","Channels"),
    ("SubTitle","T",""),
    ("Tempo","T","Tempo"),
    ("TotalSamples","I","TotalSamples"),
    ("TrackModified","D",""),
    ("TrackNumber","T","TrackOrderStr"),
    ("VBR","I","VBR"),
    ("WebArtist","T",""),
    ("WebCommercial","T",""),
    ("WebCopyright","T",""),
    ("WebFilepage","T",""),
    ("WebPayment","T",""),
    ("WebPublisher","T",""),
    ("WebRadio","T",""),
    ("WebSource","T",""),
    ("WebUser","T",""),
    ("Year","I","Year"),
)
#===============================================================================

MM_WindowMatcher = eg.WindowMatcher(
    "MediaMonkey.exe",
    None,
    u'TFMainWindow{*}',
    None,
    None,
    1,
    True,
    0,
    0
)
#===============================================================================

class EventHandler:

    def GetSongInfo(self):
        currSong = self.MM.Player.CurrentSong
        sql = ' FROM Covers WHERE IDSong="%i"' % currSong.ID
        count = 0
        Covers = []
        covers = self.MM.Database.OpenSQL('SELECT CoverType, CoverPath' + sql)
        while not covers.EOF:
            count += 1
            Covers.append((covers.ValueByIndex(0), covers.ValueByIndex(1)))
            covers.Next()
        if count > 0:
            ix = 0
            tps = [i[0] for i in Covers]
            if '3' in tps:
                ix = tps.index('3')
            elif '0' in tps:
                ix = tps.index('0')
            #artPth = path_split(currSong.Path)[0] + "\\" + Covers[ix][1]
            artPth = abspath(join(path_split(currSong.Path)[0], Covers[ix][1]))
        else:
            artPth = abspath(join(dirname(__file__), "CoverArt.png"))

        return (
            currSong.Title,
            currSong.ArtistName,
            currSong.AlbumName,
            currSong.Rating,
            currSong.TrackOrder,
            currSong.Year,
            currSong.Genre,
            currSong.Path,
            int(currSong.FileLength),
            currSong.Bitrate,
            currSong.Channels,
            currSong.SongLength,
            artPth
        )

    def OnBeforeTracksMove(self, *args):
        if self.plugin.mm_events['OnBeforeTracksMove']:
            self.TriggerEvent("BeforeTracksMove")

    def OnCompletePlaybackEnd(self):
        if self.plugin.mm_events['OnCompletePlaybackEnd']:
            self.TriggerEvent("CompletePlaybackEnd")

    def OnDownloadFinished(self, *args):
        if self.plugin.mm_events['OnDownloadFinished']:
            self.TriggerEvent("DownloadFinished")

    def OnFilterChange(self):
        if self.plugin.mm_events['OnFilterChange']:
            self.TriggerEvent("FilterChange")

    def OnChangedSelection(self):
        if self.plugin.mm_events['OnChangedSelection']:
            self.TriggerEvent("ChangedSelection")

    def OnIdle(self):
        if self.plugin.mm_events['OnIdle']:
            self.TriggerEvent("Idle")

    def OnNowPlayingModified(self):
        if self.plugin.mm_events['OnNowPlayingModified']:
            self.TriggerEvent("NowPlayingModified")

    def OnNowPlayingSelectionChanged(self):
        if self.plugin.mm_events['OnNowPlayingSelectionChanged']:
            self.TriggerEvent("NowPlayingSelectionChanged")

    def OnOptionsChange(self):
        if self.plugin.mm_events['OnOptionsChange']:
            self.TriggerEvent("OptionsChange")

    def OnPause(self):
        if self.plugin.mm_events['OnPause']:
            paused = ("Unpaused","Paused")[int(getattr(self.MM.Player, 'isPaused'))]
            self.TriggerEvent(paused)

    def OnPlay(self):
        if self.plugin.mm_events['OnPlay']:
            self.TriggerEvent("Play", self.GetSongInfo())

    def OnPlaybackEnd(self):
        if self.plugin.mm_events['OnPlaybackEnd']:
            self.TriggerEvent("PlaybackEnd")

    def OnRepeatClicked(self):
        if self.plugin.mm_events['OnRepeatClicked']:
            self.TriggerEvent("RepeatClicked")

    def OnSeek(self):
        if self.plugin.mm_events['OnSeek']:
            self.TriggerEvent("Seeked",int(getattr(self.MM.Player, 'PlaybackTime')))

    def OnShuffleClicked(self):
        if self.plugin.mm_events['OnShuffleClicked']:
            self.TriggerEvent("ShuffleClicked")

    def OnShutdown(self):
        self.plugin.StopThreads()
        if self.plugin.mm_events['OnShutdown']:
            self.TriggerEvent("Shutdown")

    def OnStop(self):
        if self.plugin.mm_events['OnStop']:
            self.TriggerEvent("Stopped")

    def OnTrackAdded(self, newTrack):
        if self.plugin.mm_events['OnTrackAdded']:
            self.TriggerEvent("TrackAdded")

    def OnTrackConverted(self, *args):
        if self.plugin.mm_events['OnTrackConverted']:
            self.TriggerEvent("TrackConverted")

    def OnTrackDeleting(self, *args):
        if self.plugin.mm_events['OnTrackDeleting']:
            self.TriggerEvent("TrackDeleting")

    def OnTrackEnd(self):
        if self.plugin.mm_events['OnTrackEnd']:
            self.TriggerEvent("TrackEnd")

    def OnTrackListFilled(self):
        if self.plugin.mm_events['OnTrackListFilled']:
            self.TriggerEvent("TrackListFilled")

    def OnTrackListFilling(self):
        if self.plugin.mm_events['OnTrackListFilling']:
            self.TriggerEvent("TrackListFilling")

    def OnTrackListModified(self):
        if self.plugin.mm_events['OnTrackListModified']:
            self.TriggerEvent("TrackListModified")

    def OnTrackListSelectionChanged(self):
        if self.plugin.mm_events['OnTrackListSelectionChanged']:
            self.TriggerEvent("TrackListSelectionChanged")

    def OnTrackProperties(self, *args):
        if self.plugin.mm_events['OnTrackProperties']:
            self.TriggerEvent("TrackProperties")

    def OnTrackSkipped(self, *args):
        if self.plugin.mm_events['OnTrackSkipped']:
            self.TriggerEvent("TrackSkipped")

#===============================================================================

class MediaMonkeyWorkerThread(eg.ThreadWorker):
    """
    Handles the COM interface in a thread of its own.
    """
    def Setup(self, plugin, events = False):
        """
        This will be called inside the thread at the beginning.
        """
        self.plugin = plugin
        self.mainThread = events
        self.events = None
        self.MM = None

        self.initMM()

        flag = True
        counter = 0
        while not self.isRunning():
            counter += 1
            if counter >= 1000:
                flag = False
                break
            eg.Utils.time.sleep(0.1)
        if self.mainThread:
            if flag and self.plugin.eg_events[7]:
                self.plugin.TriggerEvent(Text.isRunningEvt)


    def initMM(self):
        del self.MM
        del self.events
        self.events = None
        self.MM = Dispatch("SongsDB.SDBApplication")
        self.MM.ShutdownAfterDisconnect = False

        if self.mainThread:
            class SubEventHandler(EventHandler):
                MM = self.MM
                plugin = self.plugin
                TriggerEvent = self.plugin.TriggerEvent
            self.EventHandler = SubEventHandler
            self.events = DispatchWithEvents(self.MM, self.EventHandler)


    def Finish(self):
        """
        This will be called inside the thread when it finishes. It will even
        be called if the thread exits through an exception.
        """
        if self.mainThread:
            del self.events
            self.plugin.workerThread = None
        else:
            self.plugin.workerThread2 = None
        del self.MM

    def isRunning(self):
        try:
            if self.MM.IsRunning:
                return True
        except:
            return False

    def DoCommand(self, command):
        if self.isRunning():
            getattr(self.MM.Player, command)()

    def SetValue(self, command, value):
        if self.isRunning():
            setattr(self.MM.Player,command, value)

    def GetValue(self,command):
        if self.isRunning():
            return getattr(self.MM.Player, command)

    def Previous(self):
        if self.isRunning():
            if self.MM.Player.CurrentSongIndex !=0:
                self.MM.Player.Previous()
            elif self.MM.Player.isRepeat:
                self.MM.Player.CurrentSongIndex = self.MM.Player.PlaylistCount-1

    def GetSongData(self,index):
        if self.isRunning():
            repeat = self.MM.Player.isRepeat
            SongDataDict = dict([(item[2],None) for item in SONG_TABLE_FIELDS if item[2] != ""])
            curIndx = self.MM.Player.CurrentSongIndex
            count = self.MM.Player.PlaylistCount
            if not repeat:
                if (index == 1 and curIndx == count-1) or (index == -1 and curIndx == 0):
                    return None,None
            else:
                if index == 1 and curIndx == count-1:
                    curIndx = -1
                elif index == -1 and curIndx == 0:
                    curIndx = count
            index += curIndx
            tmpObject = self.MM.Player.PlaylistItems(index)

            for item in SongDataDict.iterkeys():
                itm = getattr(tmpObject,item)
                if isinstance(itm, unicode):
                    SongDataDict[item] = itm
                else:
                    if type(itm).__name__ == 'time':
                        SongDataDict[item] = repr(itm)[8:-1]
                    else:
                        SongDataDict[item] = str(itm)
            return SongDataDict, index+1

    def WriteToMMdatabase(self, command, value, flag):
        if self.isRunning():
            setattr(self.MM.Player.CurrentSong,command,value)
            self.MM.Player.CurrentSong.UpdateDB()
            if flag:
                self.MM.Player.CurrentSong.WriteTags()

    def LoadPlaylistByTitle(self, plString,repeat,shuffle,crossfade,clear):
        if self.isRunning():
            plItems = self.MM.PlaylistByTitle(plString).Tracks
            Total = plItems.Count
            if Total > 0:
                if repeat < 2:
                    self.MM.Player.isRepeat = bool(repeat)
                if crossfade < 2:
                    self.MM.Player.isCrossfade = bool(crossfade)
                if shuffle < 2:
                    self.MM.Player.isShuffle = bool(shuffle)
                if clear:
                    self.MM.Player.PlaylistClear()
                    self.MM.Player.PlaylistAddTracks(plItems)
                    self.MM.Player.Stop()
                else:
                    self.MM.Player.PlaylistAddTracks(plItems)
                if self.MM.Player.isPaused or not self.MM.Player.isPlaying:
                    self.MM.Player.Play()
            if self.plugin.eg_events[0]:
                self.plugin.TriggerEvent(Text.playlistEvt, (plString, str(Total)))

    def GetNotAccessibleTracks(self, filePath):
        if self.isRunning():
            count = 0
            try:
                MyTracks = self.MM.Database.QuerySongs("")
                file = codecs.open(filePath,encoding='utf-8', mode='w',errors='replace')
                while not MyTracks.EOF:
                    path = MyTracks.Item.Path
                    if not isfile(path):
                        count += 1
                        file.write('\t'.join((
                            str(MyTracks.Item.ID),
                            MyTracks.Item.Title,
                            path,
                        )))
                        file.write('\n')
                    MyTracks.Next()
                file.close()
            except:
                pass
            self.plugin.TriggerEvent("unaccessible",str(count))

    def DeleteSongFromLibrary(self,ID):
        if self.isRunning():
            sql = ' FROM Songs WHERE ID="%s"' % ID
            self.MM.Database.ExecSQL('DELETE'+sql)
            return self.MM.Database.OpenSQL('SELECT COUNT(*)'+sql).ValueByIndex(0)

    def AddSongToPlaylist(self, plString, skip):
        if self.isRunning():
            idSong=self.MM.Player.CurrentSong.ID
            IDPlaylist=self.MM.PlaylistByTitle(plString).ID
            if IDPlaylist <> 0:
                sql="SELECT COUNT(*) FROM PlaylistSongs WHERE PlaylistSongs.IDSong="+\
                    str(idSong)+" AND PlaylistSongs.IDPlaylist="+str(IDPlaylist)
                if self.MM.Database.OpenSQL(sql).ValueByIndex(0) == "0":
                    self.MM.PlaylistByTitle(plString).AddTrackById(idSong)
                    res = 0
                else:
                    res = 1
            else:
                res = 2
            if skip:
                self.MM.Player.Next()
            if self.plugin.eg_events[1]:
                self.plugin.TriggerEvent(self.plugin.text.resAdded[3],self.plugin.text.resAdded[res] % plString)

    def RemoveSongFromPlaylist(self, plString, skip, now_pl):
        if self.isRunning():
            Player = self.MM.Player
            idSong=Player.CurrentSong.ID
            IDPlaylist=self.MM.PlaylistByTitle(plString).ID
            if IDPlaylist <> 0:
                sql=" FROM PlaylistSongs WHERE IDPlaylist="+str(IDPlaylist)+" AND IDSong="+str(idSong)
                if self.MM.Database.OpenSQL("SELECT COUNT(*)"+sql).ValueByIndex(0) == "1":
                    self.MM.Database.ExecSQL("DELETE"+sql)
                    self.MM.MainTracksWindow.Refresh()
                    indx=Player.CurrentSongIndex
                    if indx > -1:
                        if idSong==Player.PlaylistItems(indx).ID:
                            if now_pl:
                                Player.PlaylistDelete(indx)
                        res = 0
                else:
                    res = 1
            else:
                res = 2
            if skip:
                Player.Next()
            if self.plugin.eg_events[2]:
                self.plugin.TriggerEvent(self.plugin.text.resRemoved[3],self.plugin.text.resRemoved[res] % plString)

    def RemoveSongFromNowPlaying(self, skip):
        if self.isRunning():
            Player = self.MM.Player
            res = 1
            idSong=Player.CurrentSong.ID
            indx=Player.CurrentSongIndex
            if indx > -1:
                if idSong==Player.PlaylistItems(indx).ID:
                    Player.PlaylistDelete(indx)
                    res = 0
                if skip:
                    Player.Next()
            if self.plugin.eg_events[3]:
                self.plugin.TriggerEvent(self.plugin.text.resNowPlay[2],self.plugin.text.resNowPlay[res])

    def LoadPlaylist(self,sql,repeat,crossfade,shuffle,clear,Total,plName):
        MyTracks = self.MM.Database.QuerySongs(sql)
        tmpSongList = self.MM.NewSongList
        n=0
        while not MyTracks.EOF:
            tmpSongList.Add(MyTracks.Item)
            n+=1
            if n==10:
                if clear:
                    self.MM.Player.PlaylistClear()
                    self.MM.Player.Stop()
                self.MM.Player.PlaylistAddTracks(tmpSongList)
                if repeat<2:
                    self.MM.Player.isRepeat = bool(repeat)
                if crossfade<2:
                    self.MM.Player.isCrossfade = bool(crossfade)
                if shuffle<2:
                    self.MM.Player.isShuffle = bool(shuffle)
                if self.MM.Player.isPaused or not self.MM.Player.isPlaying:
                    self.MM.Player.Play()
                del tmpSongList
                tmpSongList = self.MM.NewSongList
            MyTracks.Next()
        if n > 0 and n < 10:
            if clear:
                self.MM.Player.PlaylistClear()
                self.MM.Player.Stop()
                if repeat<2:
                    self.MM.Player.isRepeat = bool(repeat)
                if crossfade<2:
                    self.MM.Player.isCrossfade = bool(crossfade)
                if shuffle<2:
                    self.MM.Player.isShuffle = bool(shuffle)
            if self.MM.Player.isPaused or not self.MM.Player.isPlaying:
                self.MM.Player.PlaylistAddTracks(tmpSongList)
                self.MM.Player.Play()
        else:
            self.MM.Player.PlaylistAddTracks(tmpSongList)
        if self.plugin.eg_events[0]:
            self.plugin.TriggerEvent(Text.playlistEvt, (plName, str(Total)))

    def LoadFilterPlaylist(
        self,
        plName,
        mode,
        listRules,
        order,
        trend,
        crit,
        limit,
        random,
        num,
        repeat,
        shuffle,
        crossfade,
        clear,
        unitList,
        trendList
    ):
        if self.isRunning():
            propertiesList = SONG_TABLE_FIELDS
            sql=""
            op=' AND ' if mode == 0 else ' OR '
            for rule in listRules:
                rule_2 = eg.ParseString(rule[2])
                i=listRules.index(rule)
                substValues1=(op,propertiesList[rule[0]][0],rule_2)
                substValues2=(op,rule_2,propertiesList[rule[0]][0])
                substValues3=(op,propertiesList[rule[0]][0])
                dateType=propertiesList[rule[0]][1]
                emptVal = '""'  if dateType == "T" else '"-1"'
                tuplOper=("=","<>",">",">=","<","<=")

                if dateType=="D":
                    if rule[1]<6:
                        for ix in range(0,6):
                            if rule[1]==ix:
                                substValues=(op,propertiesList[rule[0]][0],tuplOper[ix],rule_2)
                                sql+="%sstrftime('%%Y-%%m-%%d %%H:%%M:%%S',%s+2415018.5)%s'%s'" % substValues
                                break
                    else:
                        substValues=(op,propertiesList[rule[0]][0],rule_2[:-1],unitList[int(rule_2[-1])])
                        if rule[1]==6:
                            sql+="%s(%s+2415018.5)>julianday('now','-%s %s','localtime')" % substValues
                        if rule[1]==7:
                            sql+="%s(%s+2415018.5)<julianday('now','-%s %s','localtime')" % substValues
                else: # (No "DateType")
                    for ix in range(0,6):
                        if rule[1]==ix:
                            substValues=(op,propertiesList[rule[0]][0],tuplOper[ix],rule_2)
                            sql+='%s%s%s"%s"' % substValues
                            break
                    if rule[1]==6:
                        sql+='%slike("%s%%",%s)' % substValues2
                    if rule[1]==7:
                        sql+='%sNOT like("%s%%",%s)' % substValues2
                    elif rule[1]==8:
                        sql+='%slike("%%%s",%s)' % substValues2
                    elif rule[1]==9:
                        sql+='%sNOT like("%%%s",%s)' % substValues2
                    elif rule[1]==10:
                        sql+='%sinstr(%s,"%s")' %  substValues1
                    elif rule[1]==11:
                        sql+='%sNOT (instr(%s,"%s"))' %  substValues1
                    elif rule[1]==12:
                        sql+='%s%s=' % substValues3 + emptVal
                    elif rule[1]==13:
                        sql+='%s%s<>' % substValues3 + emptVal
            sql=(sql[5:] if mode==0 else sql[4:])
            if random:
                sql += " ORDER BY RANDOM()"
            elif order:
                sql+=" order by "+propertiesList[crit][0]+" "+trendList[trend]
            if limit:
                sql+=" limit "+str(num)
            Total=self.MM.Database.OpenSQL("SELECT COUNT(*) FROM Songs WHERE "+sql).ValueByIndex(0)
            if int(Total) > 0:
                self.LoadPlaylist(sql,repeat,crossfade,shuffle,clear,Total,plName)

    def LoadSqlPlaylist(
        self,
        plName,
        sql,
        repeat,
        shuffle,
        crossfade,
        clear,
    ):
        if self.isRunning():
            #print sql #Debuging
            sql = eg.ParseString(sql)
            Total=self.MM.Database.OpenSQL("SELECT COUNT(*) FROM Songs WHERE "+sql).ValueByIndex(0)
            n=0
            if int(Total) > 0:
                self.LoadPlaylist(sql,repeat,crossfade,shuffle,clear,Total,plName)

    def GetStatistics(self):
        if self.isRunning():
            tracks = self.MM.Database.OpenSQL("SELECT COUNT(*) FROM Songs").ValueByIndex(0)
            albums = self.MM.Database.OpenSQL("SELECT COUNT(*) FROM Albums").ValueByIndex(0)
            #artists = self.MM.Database.OpenSQL("SELECT COUNT(*) FROM Artists").ValueByIndex(0)
            #playlists = self.MM.Database.OpenSQL("SELECT COUNT(*) FROM Playlists").ValueByIndex(0)
            return tracks,albums #,artists,playlists

    def Jubox(self,ID,clear,repeat,shuffle,crossfade,stop):
        if self.isRunning():
            sql = 'IDAlbum="%s"' % ID
            Total=self.MM.Database.OpenSQL("SELECT COUNT(*) FROM Songs WHERE "+sql).ValueByIndex(0)
            tmpSongList = self.MM.NewSongList
            if int(Total) > 0:
                if clear:
                    self.MM.Player.PlaylistClear()
                    count = 0
                else:
                    count = self.MM.Player.PlaylistCount
                MyTracks = self.MM.Database.QuerySongs(sql)
                res = (MyTracks.Item.AlbumName,MyTracks.Item.ArtistName)
                while not MyTracks.EOF:
                    tmpSongList.Add(MyTracks.Item)
                    MyTracks.Next()
                if repeat<2:
                    self.MM.Player.isRepeat = bool(repeat)
                if crossfade<2:
                    self.MM.Player.isCrossfade = bool(crossfade)
                if shuffle<2:
                    shuffle = bool(shuffle)
                    self.MM.Player.isShuffle = shuffle
                else:
                    shuffle = self.MM.Player.isShuffle
                self.MM.Player.PlaylistAddTracks(tmpSongList)
                if stop:
                    if not shuffle:
                        self.MM.Player.CurrentSongIndex = count
                    else:
                        self.MM.Player.CurrentSongIndex = randint(count,count+int(Total)-1)
                if self.MM.Player.isPaused or not self.MM.Player.isPlaying:
                    self.MM.Player.Play()
                if self.plugin.eg_events[4]:
                    self.plugin.TriggerEvent(Text.albumEvt, res)

    def SongJubox(self,ID,clear,stop):
        if self.isRunning():
            sql = 'ID="%s"' % ID
            res = None
            Total=self.MM.Database.OpenSQL("SELECT COUNT(*) FROM Songs WHERE "+sql).ValueByIndex(0)
            if int(Total) > 0:
                if clear:
                    self.MM.Player.PlaylistClear()
                MyTrack = self.MM.Database.QuerySongs(sql)
                res = (MyTrack.Item.Title,MyTrack.Item.AlbumName,MyTrack.Item.ArtistName)
                count = self.MM.Player.PlaylistCount
                self.MM.Player.PlaylistAddTrack(MyTrack.Item)
                if stop:
                    self.MM.Player.CurrentSongIndex = count
                if self.MM.Player.isPaused or not self.MM.Player.isPlaying:
                    self.MM.Player.Play()
                if self.plugin.eg_events[5]:
                    self.plugin.TriggerEvent(Text.songEvt, res)

    def ExportAlbumList(self,filePath):
        if self.isRunning():
            count = 0
            try:
                albums=self.MM.Database.OpenSQL("SELECT ID,Artist,Album FROM Albums")
                file = codecs.open(filePath,encoding='utf-8', mode='w',errors='replace')
                while not albums.EOF: #Structure = ID, Artist, Album
                    count += 1
                    artist = albums.ValueByIndex(1)
                    artist = artist if artist else Text.unknArtist
                    album = albums.ValueByIndex(2)
                    album = album if album else Text.unknAlbum
                    file.write('\t'.join((
                        str(albums.ValueByIndex(0)),
                        album,
                        artist
                    )))
                    file.write('\n')
                    albums.Next()
                file.close()
            except:
                pass
            if self.plugin.eg_events[6]:
                self.plugin.TriggerEvent(self.plugin.text.jukeboxEvt, str(count))

    def ExportSongList(self,filePath):
        if self.isRunning():
            count = 0
            try:
                songs=self.MM.Database.QuerySongs("")
                file = codecs.open(filePath,encoding='utf-8', mode='w',errors='replace')
                while not songs.EOF:
                    count += 1
                    artist = songs.Item.ArtistName
                    artist = artist if artist else Text.unknArtist
                    file.write('\t'.join((
                        str(songs.Item.ID),
                        songs.Item.Title,
                        artist
                    )))
                    file.write('\n')
                    songs.Next()
                file.close()
            except:
                pass
            if self.plugin.eg_events[6]:
                self.plugin.TriggerEvent(self.plugin.text.jukeboxEvt, str(count))

    def GetPlaylists(self,flag1 = True, flag2 = True):
        if self.isRunning():
            sql = 'SELECT PlaylistName FROM Playlists'
            if flag1 or flag2:
                sql += ' WHERE '
                if flag1:
                    sql += 'Persistent IS NULL'
                    if flag2:
                        sql+=' AND '
                if flag2:
                    sql += 'NOT IDFilter IS NULL'
            playlists = []
            try:
                dbPlaylists = self.MM.Database.OpenSQL(sql)
                while not dbPlaylists.EOF:
                    playlists.append(dbPlaylists.ValueByIndex(0))
                    dbPlaylists.Next()
            except:
                pass
            return playlists
#===============================================================================

class Text:
    oldVersion = '''You have installed an old version of MediaMonkey.
This plugin requires MediaMonkey version 4.0 or later.
Please install latest version of MediaMonkey and then restart EventGhost !'''
    vbsFile = '''The new version no longer needs for its work the file "EventGhost.vbs",
which is located in the folder
%s
Please remove this file and restart MediaMonkey (if running).
Then restart EventGhost !'''
    noMM = '''It seems that you have not installed MediaMonkey.
This plugin requires MediaMonkey version 4.0 or later.
Please install latest version of MediaMonkey and then restart EventGhost !'''
    label_mm = "Trigger these MediaMonkey application's events:"
    label_eg = "Trigger these plugin's events:"
    unknAlbum = "<unknown album>"
    unknArtist = "<unknown artist>"
    errorNoWindow = "Couldn't find MediaMonkey window"
    errorConnect = "MediaMonkey is not running or not connected"
    mainGrpName = "Main control of MediaMonkey"
    mainGrpDescr = "Actions for main control of MediaMonkey."
    levelGrpName = "Another control of MediaMonkey"
    levelGrpDescr = (
        "More actions for control of MediaMonkey"
        " (volume, balance, seek, ...)."
    )
    extrGrpName = "Writing to MM database"
    extrGrpDescr = (
        "Actions for writing various parameters to MediaMonkey's database."
    )
    infoGrpName = "Information retrieval"
    infoGrpDescr = (
        "Retrieve information about the MM player settings and"
        " information about songs in the MM playlist."
    )
    labelsA = (
        'ID     ',
        'Album',
        'Artist',
    )
    labelsS = (
        'ID     ',
        'Song title',
        'Artist',
    )
    labelsU = (
        'ID     ',
        'Song title',
        'File path',
    )
    toolTipFolder = "Press button and browse to select file ..."
    browseTitle = "Selected file:"
    please = "Please be patient"
    close = "Close"
    popup = "Add now"
    popup2 = "Delete from library"
    refresh = "Refresh"
    sepLabel = 'Character or string to use as delimiter (default is "\\n"):'
    resType = "Result return as one string (otherwise as list)"
    filepath = "File path"
    filename = "File name"
    playlistEvt = 'playlist'
    isRunningEvt = 'IsRunning'
    albumEvt = 'album_added'
    songEvt = 'song_added'
    jukeboxEvt = 'jukebox'
    playlistEvtTxt = 'Playlist loaded'
    albumEvtTxt = 'Jukebox - album added'
    songEvtTxt = 'Jukebox - song added'
    jukeboxEvtTxt = 'Jukebox - list exported'
    mmIsRunning = 'MediaMonkey is running'
    resAdded = (
        "Track added to playlist %s",
        "Track already exists in playlist %s",
        "Playlist %s does not exist",
        "track_added"
    )
    resRemoved = (
        "Track removed from playlist %s",
        "Track does not exist in playlist %s",
        "Playlist %s does not exist",
        "track_removed"
    )
    resNowPlay = (
        "Track removed from Now Playing playlist",
        "Track not removed from Now Playing playlist",
        "removed_from_now_playing"
    )
    sepToolTip = '''Optionally, enter the character
or string to be used as a delimiter (separator), between items.
Default is to use the character "\\n" (new line).'''
    repeat = "Continous playback"
    shuffle = "Shuffle tracks"
    crossfade = "Crossfade"
    clearPlaylist = "Clear the Now Playing playlist before adding new tracks"
    stopPlaying = "Stop playing just sounding songs"
    random = "Order by random"
    randomToolTip = '''(True Shuffle)  Shuffles the playlist (like cards) before starting playback.
This allows us to see what song will play next, and what played previously.
Whereas selecting the "%s" checkbox will cause the player to jump randomly through a playlist.'''
    SongTableFields = (
        "Album",
        "Album Artist",
        "Artist",
        "Audio CD Track",
        "Author",
        "Band",
        "Bitrate",
        "BPM",
        "Broadcast",
        "Cache Name",
        "Cache Status",
        "Comment",
        "Conductor",
        "Copyright",
        "Copyrighted",
        "Custom1",
        "Custom2",
        "Custom3",
        "Custom4",
        "Custom5",
        "Date Added",
        "Disc Number",
        "Encoded By",
        "Encoder",
        "File Length",
        "File Modified",
        "Gapless Bytes",
        "Genre",
        "Group Desc",
        "Song ID",
        "ID Album",
        "ID Folder",
        "ID Media",
        "Initial Key",
        "Involved People",
        "ISRC",
        "Language",
        "Last Time Played",
        "Lyricist",
        "Lyrics",
        "Max Sample",
        "Media Type",
        "Mood",
        "Normalize Album",
        "Normalize Track",
        "Occasion",
        "Original Artist",
        "Original FileLength",
        "Original",
        "Original Lyricist",
        "Original Title",
        "Original Year",
        "Playback Position",
        "Play Counter",
        "Post Gap",
        "Pre Gap",
        "Preview Length",
        "Preview Name",
        "Preview StartTime",
        "Preview State",
        "Publisher",
        "Quality",
        "Rating",
        "Rating String",
        "Remixer",
        "Sampling Frequency",
        "Seekable",
        "Sign Part1",
        "Sign Part2",
        "Sign Part3",
        "Sign Part4",
        "Sign Type",
        "Song Length",
        "Song Path",
        "Song Title",
        "Stereo",
        "SubTitle",
        "Tempo",
        "Total Samples",
        "Track Modified",
        "Track Number",
        "VBR",
        "Web Artist",
        "Web Commercial",
        "Web Copyright",
        "Web Filepage",
        "Web Payment",
        "Web Publisher",
        "Web Radio",
        "Web Source",
        "Web User",
        "Year",
    )
#====================================================================
def getHwnd():
    if eg.document is not None:
        return eg.document.frame.GetHandle()

class MediaMonkey(eg.PluginBase):
    manFlg = False
    autoFlg = False
    stopFlg = False
    workerThread = None
    workerThread2 = None
    checkTask = None
    checkOpened = None
    text = Text

    def __init__(self):
        group = self.AddGroup(
            self.text.mainGrpName,
            self.text.mainGrpDescr
        )
        group.AddAction(Start)
        group.AddAction(Exit)
        group.AddAction(Play)
        group.AddAction(TogglePlay)
        group.AddAction(DiscretePause)
        group.AddAction(Stop)
        group.AddAction(StopAfterCurrent)
        group.AddAction(Next)
        group.AddAction(Previous)
        group.AddAction(LoadPlaylist)
        group.AddAction(LoadPlaylistByFilter)
        group.AddAction(LoadPlaylistBySql)
        group.AddAction(AddCurrentSongToPlaylist)
        group.AddAction(RemoveCurrentSongFromPlaylist)
        group.AddAction(RemoveCurrentSongFromNowPlaying)
        group.AddAction(Jukebox)
        group.AddAction(SongJukebox)
        group.AddAction(SendKeys)
        group = self.AddGroup(
            self.text.levelGrpName,
            self.text.levelGrpDescr
        )
        group.AddAction(ToggleMute)
        group.AddAction(SetVolume)
        group.AddAction(VolumeUp)
        group.AddAction(VolumeDown)
        group.AddAction(SetBalance)
        group.AddAction(SetRepeat)
        group.AddAction(SetShuffle)
        group.AddAction(SetAutoDJ)
        group.AddAction(SetCrossfade)
        group.AddAction(BalanceRight)
        group.AddAction(BalanceLeft)
        group.AddAction(Seek)
        group = self.AddGroup(
            self.text.extrGrpName,
            self.text.extrGrpDescr
        )
        group.AddAction(WritingToMM)
        group = self.AddGroup(
            self.text.infoGrpName,
            self.text.infoGrpDescr
        )
        group.AddAction(GetPlaylists)
        group.AddAction(GetBasicStatistics)
        group.AddAction(GetNotAccessibleTracks)
        group.AddActionsFromList(ACTIONS)
        group.AddAction(GetStatus)
        group.AddActionsFromList(ACTIONS2)
        group.AddAction(GetDetailSongInfo)
        group.AddAction(GetClassificationInfo)
        group.AddAction(GetTechnicalSongInfo)
        group.AddAction(GetUniversal)

        MMpath, ver = self.GetPathAndVersion()
        if ver:
            if ver < "4.0":
                mssgs = [self.text.oldVersion]
                if isfile(MMpath+"\\EventGhost.vbs"):
                    mssgs.extend((" ", self.text.vbsFile % MMpath))
                self.info.Start = self.DummyStart #Start disabling
                hwnd = getHwnd()
                wx.CallAfter(
                    MessageBox,
                    hwnd,
                    "\n".join(mssgs),
                    "EventGhost - MediaMonkey plugin",
                    48
                )
                raise self.Exceptions.InitFailed
            elif isfile(MMpath+"\\EventGhost.vbs"):
                self.info.Start = self.DummyStart #Start disabling
                hwnd = getHwnd()
                wx.CallAfter(
                    MessageBox,
                    hwnd,
                    self.text.vbsFile % MMpath,
                    "EventGhost - MediaMonkey plugin",
                    48
                )
                raise self.Exceptions.InitFailed

        else:
            self.info.Start = self.DummyStart #Start disabling
            hwnd = getHwnd()
            wx.CallAfter(
                MessageBox,
                hwnd,
                self.text.noMM,
                "EventGhost - MediaMonkey plugin",
                48
            )
            raise self.Exceptions.InitFailed
        icoFile = abspath(join(dirname(__file__), "CoverArt.png"))
        if not isfile(icoFile):
            mmIco = self.info.icon.key
            stream = open(icoFile, "wb")
            stream.write(mmIco.decode('base64'))
            stream.close()


    def DummyStart(self):
        pass


    def __start__(
        self,
        events = 4 * [True] + 23 * [False],
        events2 = 8 * [True]
    ):
        self.manFlg = False
        self.autoFlg = False
        self.stopFlg = False
        self.volume = None
        self.muted = False
        if isinstance(events, bool):# For compatibility with old version !!!
            events = 4 * [True] + 23 * [False]
            events2 = 8 * [True]
        if len(events2) == 7:# For compatibility with version 0.3.3 !!!
            events2.append(True)
        self.mm_events = dict(zip(MM_EVENTS, events))
        self.eg_events = events2
        self.checkTask = eg.scheduler.AddTask(1, self.checkIsRunning)


    def GetPathAndVersion(self):
        """
        Get the path of LAN Chat's installation directory through querying
        the Windows registry.
        """
        MMpath = ""
        ver = ""
        try:
            mmp = OpenKey(
                HKEY_LOCAL_MACHINE,
                "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MediaMonkey_is1"
            )
            try:
                MMpath = QueryValueEx(mmp, "InstallLocation")[0]
                ver = QueryValueEx(mmp, "DisplayVersion")[0]
            except:
                pass
            CloseKey(mmp)
        except:
            pass
        return MMpath + "Scripts\\Auto", ver


    def checkWinOpened(self):
        hwnds = MM_WindowMatcher()
        if hwnds:
            dummy = SendMessageTimeout(hwnds[0], WM_SYSCOMMAND, SC_MINIMIZE, 20)
            self.checkOpened = None
        else:
            self.checkOpened = eg.scheduler.AddTask(1, self.checkWinOpened)


    def isRunning(self):
        try:
            return FindWindow("TFMainWindow", "MediaMonkey") > 0
        except:
            return False


    def checkIsRunning(self):
        self.checkTask = eg.scheduler.AddTask(2, self.checkIsRunning) # must run continuously !
        if not self.isRunning():
                if self.stopFlg:
                    self.manFlg = False
                    self.stopFlg = False
                    self.autoFlg = False
                    self.StopThreads()
        elif not self.autoFlg:
            if not self.manFlg:
                self.manFlg = True
                eg.scheduler.AddShortTask(0.05, self.startWorkerThread)


    def startWorkerThread(self):
        self.workerThread = MediaMonkeyWorkerThread(self, True)
        self.workerThread.Start(1000.0)


    def Configure(self, events = 4 * [True] + 23 * [False], events2 = 8*[True]):
        if isinstance(events, bool): # For compatibility with old version !!!
            events = 4 * [True] + 23 * [False]
            events2 = 8 * [True]
        panel = eg.ConfigPanel(self)
        label_mm = wx.StaticText(panel, -1, self.text.label_mm)
        choices = MM_EVENTS
        eventsCtrl = wx.CheckListBox(
            panel,
            -1,
            choices = choices,
        )
        for i in range(len(events)):
            eventsCtrl.Check(i, events[i])
        label_eg = wx.StaticText(panel, -1, self.text.label_eg)
        choices = (
            self.text.playlistEvtTxt,
            self.text.resAdded[0] % "",
            self.text.resRemoved[0] % "",
            self.text.resNowPlay[0],
            self.text.albumEvtTxt,
            self.text.songEvtTxt,
            self.text.jukeboxEvtTxt,
            self.text.mmIsRunning,
        )
        events2Ctrl = wx.CheckListBox(
            panel,
            -1,
            choices = choices,
        )
        for i in range(len(events2)):
            events2Ctrl.Check(i, events2[i])

        Sizer = wx.FlexGridSizer(2, 2, 1, 10)
        Sizer.AddGrowableRow(1)
        Sizer.AddGrowableCol(0)
        Sizer.AddGrowableCol(1)
        Sizer.Add(label_mm)
        Sizer.Add(label_eg)
        Sizer.Add(eventsCtrl, 1, wx.EXPAND)
        Sizer.Add(events2Ctrl, 1, wx.EXPAND)
        panel.sizer.Add(Sizer,1,wx.EXPAND)

        while panel.Affirmed():
            tmpList = []
            for i in range(len(events)):
                tmpList.append(eventsCtrl.IsChecked(i))
            tmpList2 = []
            for i in range(len(events2)):
                tmpList2.append(events2Ctrl.IsChecked(i))
            panel.SetResult(
                tmpList,
                tmpList2
            )


    def SendCommand(self, command):
        if self.workerThread:
            self.workerThread.CallWait(partial(self.workerThread.DoCommand, command),1000)
        else:
            self.PrintError("self.workerThread is None !")


    def SetValue(self, command, value):
        if self.workerThread:
            self.workerThread.CallWait(partial(self.workerThread.SetValue, command, value),1000)


    def GetValue(self, command):
        if self.workerThread:
            return self.workerThread.CallWait(partial(self.workerThread.GetValue, command),1000)


    def GetIsRunning(self):
        if self.workerThread:
            return self.workerThread.CallWait(partial(self.workerThread.isRunning),1000)


    def GetSongData(self,index):
        if self.workerThread:
            return self.workerThread.CallWait(partial(self.workerThread.GetSongData, index),1000)


    def Jubox(self, ID, clear=True,repeat=2,shuffle=2,crossfade=2,stop = True):
        if self.workerThread:
            self.workerThread.Call(partial(
                self.workerThread.Jubox,
                ID,
                clear,
                repeat,
                shuffle,
                crossfade,
                stop
            ))


    def SongJubox(self, ID,clear=False,stop = True):
        if self.workerThread:
            self.workerThread.Call(partial(
                self.workerThread.SongJubox,
                ID,
                clear,
                stop
            ))


    def DeleteSong(self, ID):
        if self.workerThread:
            res = self.workerThread.CallWait(partial(
                self.workerThread.DeleteSongFromLibrary,
                ID,
            ), 1000)
            return res


    def __stop__(self):
        self.StopThreads()
        if self.checkTask:
            eg.scheduler.CancelTask(self.checkTask)
        self.checkTask = None
        if self.checkOpened:
            eg.scheduler.CancelTask(self.checkOpened)
        self.checkOpened = None


    def StopThreads(self, event = None):
        if self.workerThread:
            self.workerThread.Stop()
        if self.workerThread2:
            self.workerThread2.Stop()
#===============================================================================

class Start(eg.ActionBase):
    name = "Start MediaMonkey"
    description = "Starts MediaMonkey."

    def __call__(self, choice=True):
        if not self.plugin.isRunning() and not self.plugin.autoFlg:
            self.plugin.autoFlg = True
            eg.scheduler.AddShortTask(0.1, self.plugin.startWorkerThread)
            if choice:
                self.plugin.checkOpened = eg.scheduler.AddTask(
                    1,
                    self.plugin.checkWinOpened
                )

    def Configure(self, choice=True):
        panel = eg.ConfigPanel(self)
        choiceCtrl = wx.CheckBox(panel, -1, self.text.choice_label)
        choiceCtrl.SetValue(choice)
        panel.sizer.Add(choiceCtrl, 0, wx.TOP, 20)

        while panel.Affirmed():
            panel.SetResult(choiceCtrl.GetValue())

    class text:
        choice_label = "Start minimized"
#===============================================================================

class Exit(eg.ActionBase):
    name = "Quit (close) MediaMonkey"
    description = "Quits (closes) MediaMonkey."

    def __call__(self, choice = None):
        hwnds = MM_WindowMatcher()
        if hwnds:
            self.plugin.stopFlg = True
            self.plugin.StopThreads()
            CloseHwnd(hwnds[0])
        else:
            raise self.Exceptions.ProgramNotRunning


    def GetLabel(self):
        return self.name
#====================================================================

class Play(eg.ActionBase):
    name = "Play"
    description = "Play."

    def __call__(self):
        self.plugin.SendCommand('Play')
#====================================================================

class TogglePlay(eg.ActionBase):
    name = "Toggle Play"
    description = "Toggles between play and pause."

    def __call__(self):
        if  not self.plugin.GetValue('isPlaying'):
            # Play
            return self.plugin.SendCommand('Play')
        else:
            # Toggle Play/Pause
            return self.plugin.SendCommand('Pause')
#====================================================================

class DiscretePause(eg.ActionBase):
    name = "Discrete Pause"
    description = (
        "Pauses MediaMonkey if it is playing, but won't do anything if "
        "MediaMonkey is already paused."
    )

    def __call__(self):
        if self.plugin.GetValue('isPlaying'):
            if not self.plugin.GetValue('isPaused'):
                return self.plugin.SendCommand('Pause')
#====================================================================

class Stop(eg.ActionBase):
    name = "Stop"
    description = "Stop MediaMonkey Playback."

    def __call__(self):
        return self.plugin.SendCommand('Stop')
#====================================================================

class StopAfterCurrent(eg.ActionBase):
    name = "Stop after current song"
    description = "Stop MediaMonkey Playback After Current Song."

    def __call__(self):
        return self.plugin.SetValue('StopAfterCurrent', True)
#====================================================================

class Next(eg.ActionBase):
    name = "Next"
    description = "Play next track."

    def __call__(self):
        self.plugin.SendCommand('Next')
#====================================================================

class Previous(eg.ActionBase):
    name = "Previous"
    description = "Play previous track."

    def __call__(self):
        if self.plugin.workerThread:
            self.plugin.workerThread.Call(partial(self.plugin.workerThread.Previous))
#====================================================================

class ToggleMute(eg.ActionBase):
    name = "Toggle Mute"
    description = "Toggle MediaMonkey mute on and off."

    def __call__(self):
        if not self.plugin.muted:
            self.plugin.volume=self.plugin.GetValue('Volume')
            self.plugin.SetValue('Volume',0)
            self.plugin.muted=True
        else:
            self.plugin.SetValue('Volume',self.plugin.volume)
            self.plugin.muted=False
#====================================================================

class SetVolume(eg.ActionBase):
    name = "Set Volume Level"
    description = "Sets the volume to a percentage (%) from 0 to 100."

    def __call__(self, volume=50.00):
        self.plugin.SetValue('Volume',volume/100)
        if volume!=0:
            self.plugin.muted=False

    def GetLabel(self, volume):
        return self.text.label_tree+str(int(volume))+"%"

    def Configure(self, volume=50.0):
        panel = eg.ConfigPanel(self)
        volumeCtrl = eg.SpinNumCtrl(
            panel,
            -1,
            volume,
            max=100.0,
            fractionWidth=1
        )
        panel.AddLabel(self.text.label_conf)
        panel.AddCtrl(volumeCtrl)
        while panel.Affirmed():
            panel.SetResult(volumeCtrl.GetValue())

    class text:
        label_tree="Set volume "
        label_conf="Volume Level:"
#====================================================================

class VolumeUp(eg.ActionBase):
    name = "Volume up "
    description = "Increase volume by x%."

    def __call__(self, step=10.0):
        if step>0:
            self.plugin.muted=False
            volume=self.plugin.GetValue('Volume')
            if volume<1:
                if volume>(1-step/100):
                    volume=1
                else:
                    volume+=step/100
                self.plugin.SetValue('Volume',volume)

    def GetLabel(self, step):
        return self.text.label_tree+str(int(step))+"%"

    def Configure(self, step=10.0):
        panel = eg.ConfigPanel(self)
        volumeCtrl = eg.SpinNumCtrl(panel, -1, step, max=100.0,fractionWidth=1)
        panel.AddLabel(self.text.label_conf)
        panel.AddCtrl(volumeCtrl)
        while panel.Affirmed():
            panel.SetResult(volumeCtrl.GetValue())

    class text:
        label_tree="Volume up "
        label_conf="Volume step:"
#====================================================================

class VolumeDown(eg.ActionBase):
    name = "Volume down "
    description = "Decrease volume by x%."

    def __call__(self, step=-10.0):
        volume=self.plugin.GetValue('Volume')
        if volume>0:
            if volume<abs(step)/100:
                volume=0
            else:
                volume+=step/100
            self.plugin.SetValue('Volume',volume)

    def GetLabel(self, step):
        return self.text.label_tree+str(abs(int(step)))+"%"

    def Configure(self, step=-10.0):
        panel = eg.ConfigPanel(self)
        volumeCtrl = eg.SpinNumCtrl(
            panel,
            -1,
            step,
            max=0.0,
            min=-100.0,
            fractionWidth=1
        )
        panel.AddLabel(self.text.label_conf)
        panel.AddCtrl(volumeCtrl)
        while panel.Affirmed():
            panel.SetResult(volumeCtrl.GetValue())

    class text:
        label_tree="Volume down "
        label_conf="Volume step:"
#====================================================================

class SetBalance(eg.ActionBase):
    name = "Set Balance"
    description = "Sets the balance."

    def __call__(self, balance=0.0):
        self.plugin.SetValue('Panning', balance/100)

    def GetLabel(self, balance):
        return self.text.label_tree+str(int(balance))+"%"

    def Configure(self, balance=0.0):
        panel = eg.ConfigPanel(self)
        balanceCtrl = eg.SpinNumCtrl(
            panel,
            -1,
            balance,
            max=100.0,
            min=-100.0,
            fractionWidth=1
        )
        panel.AddLabel(self.text.label_conf)
        panel.AddCtrl(balanceCtrl)
        while panel.Affirmed():
            panel.SetResult(balanceCtrl.GetValue())

    class text:
        label_tree="Set balance "
        label_conf = "Balance (-100 ... 100):"
#====================================================================

class SetShuffle(eg.ActionBase):
    name = "Set shuffle tracks"
    description = "Set shuffle tracks mode to on or off or toggle."

    def __call__(self, swt = 0):
        swt = not self.plugin.GetValue('isShuffle') if swt == 2 else swt == 0
        self.plugin.SetValue('isShuffle', swt)
        return swt

    def GetLabel(self, switch):
        return "Set shuffle tracks: "+self.text.choices[switch]

    def Configure(self, switch=0):
        text=self.text
        panel = eg.ConfigPanel(self)
        radioBox = wx.RadioBox(
            panel,
            -1,
            text.radiobox,
            choices=text.choices,
            style=wx.RA_SPECIFY_ROWS
        )
        radioBox.SetSelection(switch)
        panel.sizer.Add(radioBox, 0, wx.EXPAND)
        while panel.Affirmed():
            panel.SetResult(radioBox.GetSelection(),)

    class text:
        radiobox = "Set shuffle tracks to state ..."
        choices = (
            "ON",
            "OFF",
            "Toggle"
        )
#====================================================================

class SetRepeat(eg.ActionBase):
    name = "Set continous playback"
    description = "Turn continuous mode on or off"

    def __call__(self, switch=0):
        self.plugin.SetValue('isRepeat', switch == 0)

    def GetLabel(self, switch):
        return "Set continous playback "+("ON" if switch==0 else "OFF")

    def Configure(self, switch=0):
        text=Text
        panel = eg.ConfigPanel(self)
        radioBox = wx.RadioBox(
            panel,
            -1,
            self.text.radiobox,
            choices=[self.text.RepeatON, self.text.RepeatOFF],
            style=wx.RA_SPECIFY_ROWS
        )
        radioBox.SetSelection(switch)
        panel.sizer.Add(radioBox, 0, wx.EXPAND)
        while panel.Affirmed():
            panel.SetResult(radioBox.GetSelection(),)

    class text:
        radiobox = "Set continous playback to state ..."
        RepeatON = "ON"
        RepeatOFF = "OFF"
#====================================================================

class SetAutoDJ(eg.ActionBase):
    name = "Set AutoDJ"
    description = "Turn the AutoDJ on or off."

    def __call__(self, switch=0):
        self.plugin.SetValue('isAutoDJ', switch == 0)

    def GetLabel(self, switch):
        return "Set AutoDJ "+("ON" if switch==0 else "OFF")

    def Configure(self, switch=0):
        text=Text
        panel = eg.ConfigPanel(self)
        radioBox = wx.RadioBox(
            panel,
            -1,
            self.text.radiobox,
            choices=[self.text.AutoDJON, self.text.AutoDJOFF],
            style=wx.RA_SPECIFY_ROWS
        )
        radioBox.SetSelection(switch)
        panel.sizer.Add(radioBox, 0, wx.EXPAND)
        while panel.Affirmed():
            panel.SetResult(radioBox.GetSelection(),)

    class text:
        radiobox = "Set AutoDJ to state ..."
        AutoDJON = "ON"
        AutoDJOFF = "OFF"
#====================================================================

class SetCrossfade(eg.ActionBase):
    name = "Set Crossfade"
    description = "Sets the crossfade to on or off."

    def __call__(self, switch=0):
        self.plugin.SetValue('isCrossfade', switch == 0)

    def GetLabel(self, switch):
        return "Set Crossfade "+("ON" if switch==0 else "OFF")

    def Configure(self, switch=0):
        text=Text
        panel = eg.ConfigPanel(self)
        radioBox = wx.RadioBox(
            panel,
            -1,
            self.text.radiobox,
            choices=[self.text.CrossfadeON, self.text.CrossfadeOFF],
            style=wx.RA_SPECIFY_ROWS
        )
        radioBox.SetSelection(switch)
        panel.sizer.Add(radioBox, 0, wx.EXPAND)
        while panel.Affirmed():
            panel.SetResult(radioBox.GetSelection(),)

    class text:
        radiobox = "Set Crossfade to state ..."
        CrossfadeON = "ON"
        CrossfadeOFF = "OFF"
#====================================================================

class BalanceRight(eg.ActionBase):
    name = "Balance Right x%"
    description = "Shift balance to the right x%."

    def __call__(self, step=10.0):
        if step>0:
            balance=self.plugin.GetValue('Panning')
            if balance<1:
                if balance>(1-step/100):
                    balance=1
                else:
                    balance+=step/100
                self.plugin.SetValue('Panning',balance)

    def GetLabel(self, step):
        return self.text.label_tree+str(int(step))+"%"

    def Configure(self, step=10.0):
        panel = eg.ConfigPanel(self)
        balanceCtrl = eg.SpinNumCtrl(
            panel,
            -1,
            step,
            max=100.0,
            fractionWidth=1
        )
        panel.AddLabel(self.text.label_conf)
        panel.AddCtrl(balanceCtrl)
        while panel.Affirmed():
            panel.SetResult(balanceCtrl.GetValue())

    class text:
        label_tree="Balance right "
        label_conf = "Balance step:"
#====================================================================

class BalanceLeft(eg.ActionBase):
    name = "Balance Left x%"
    description = "Shift balance to the left x%."

    def __call__(self, step=10.0):
        if step>0:
            balance=self.plugin.GetValue('Panning')
            if balance>-1:
                if balance<(step/100-1):
                    balance=-1
                else:
                    balance+=-step/100
                self.plugin.SetValue('Panning',balance)

    def GetLabel(self, step):
        return self.text.label_tree+str(int(step))+"%"

    def Configure(self, step=10.0):
        panel = eg.ConfigPanel(self)
        balanceCtrl = eg.SpinNumCtrl(
            panel,
            -1,
            step,
            max=100.0,
            fractionWidth=1
        )
        panel.AddLabel(self.text.label_conf)
        panel.AddCtrl(balanceCtrl)
        while panel.Affirmed():
            panel.SetResult(balanceCtrl.GetValue())

    class text:
        label_tree="Balance left "
        label_conf = "Balance step:"
#====================================================================

class Seek(eg.ActionBase):
    name = "Seek Forward or Backward x%"
    description = "Seek Forward or Backward x%."

    def __call__(self, step=10.0, direction=0):
        length=self.plugin.GetValue('CurrentSongLength')
        pos=self.plugin.GetValue('PlaybackTime')
        if direction: #Backward
            if pos>length*step/100:
                self.plugin.SetValue('PlaybackTime',pos-length*step/100)
            else:
                self.plugin.SetValue('PlaybackTime',0)
        else:         #Forward
            if pos<length-length*step/100:
                self.plugin.SetValue('PlaybackTime',pos+length*step/100)
            else:
                self.plugin.SetValue('PlaybackTime',length-500)

    def GetLabel(self, step, direction):
        return self.text.tree_lab1\
            +(self.text.tree_lab2 if direction else self.text.tree_lab3)\
            +" "+str(int(step))+"%"

    def Configure(self, step=10.0, direction=0):
        text=Text
        panel = eg.ConfigPanel(self)
        seekCtrl = eg.SpinNumCtrl(panel, -1, step, max=100.0, fractionWidth=1)
        radioBox = wx.RadioBox(
            panel,
            -1,
            self.text.radiobox,
            choices=[self.text.btnForward, self.text.btnBackward],
            style=wx.RA_SPECIFY_ROWS
        )
        panel.AddLabel(self.text.label)
        panel.AddCtrl(seekCtrl)
        radioBox.SetSelection(direction)
        panel.sizer.Add(radioBox, 0, wx.EXPAND)
        while panel.Affirmed():
            panel.SetResult(seekCtrl.GetValue(),radioBox.GetSelection())

    class text:
        radiobox = "Seek direction"
        btnForward = "Forward"
        btnBackward = "Backward"
        label = "Seek step (%):"
        tree_lab1 = "Seek "
        tree_lab2 = "backward"
        tree_lab3 = "forward"
#====================================================================

class GetPlaylists(eg.ActionBase):
    name = "Get playlist names"
    description = "Gets list of playlist names."

    def __call__(self, flag1 = True, flag2 = True):
        if self.plugin.workerThread:
            playlists = self.plugin.workerThread.CallWait(partial(
                self.plugin.workerThread.GetPlaylists,
                flag1,
                flag2
            ),60)
            return playlists


    def GetLabel(self, flag1, flag2):
        label = "%s: %s, %s" % (self.name, str(flag1), str(flag2))
        return label

    def Configure(self, flag1 = True, flag2 = True):
        panel = eg.ConfigPanel(self)
        autoCtrl = wx.CheckBox(panel, -1, self.text.auto)
        autoCtrl.SetValue(flag1)
        panel.sizer.Add(autoCtrl, 0, wx.TOP, 20)
        importedCtrl = wx.CheckBox(panel, -1, self.text.imported)
        importedCtrl.SetValue(flag1)
        panel.sizer.Add(importedCtrl, 0, wx.TOP, 20)

        while panel.Affirmed():
            panel.SetResult(
                autoCtrl.GetValue(),
                importedCtrl.GetValue(),
            )

    class text:
        auto = "Ignore auto-playlists"
        imported = "Ignore imported playlists"
#====================================================================

class GetBasicStatistics(eg.ActionBase):
    name = "Get Basic Statistics"
    description = "Get Basic Statistics (number of tracks and albums in the database)."

    def __call__(self,sep='',res = True):
        if self.plugin.workerThread:
            if sep == '':
                sep = '\n'
            tracks, albums = self.plugin.workerThread.CallWait(partial(self.plugin.workerThread.GetStatistics),60)
            return self.text.tracks % tracks+sep+self.text.albums % albums

    def GetLabel(self,sep=''):
        return self.name

    def Configure(self, sep='', res = True):
        panel = eg.ConfigPanel(self)
        sepLbl = wx.StaticText(panel,-1,self.plugin.text.sepLabel)
        sepCtrl = wx.TextCtrl(panel,-1,sep)
        sepLbl.SetToolTip(wx.ToolTip(self.plugin.text.sepToolTip))
        sepCtrl.SetToolTip(wx.ToolTip(self.plugin.text.sepToolTip))
        resCtrl = wx.CheckBox(panel, -1, self.plugin.text.resType)
        resCtrl.SetValue(res)


        def onResCtrl(evt = None):
            enable = resCtrl.GetValue()
            sepCtrl.Enable(enable)
            sepLbl.Enable(enable)
            if evt:
                evt.Skip()
        resCtrl.Bind(wx.EVT_CHECKBOX, onResCtrl)
        onResCtrl()

        mySizer = wx.BoxSizer(wx.HORIZONTAL)
        panel.sizer.Add(resCtrl, 0, wx.TOP, 30)
        panel.sizer.Add(mySizer,0,wx.TOP,15)
        mySizer.Add(sepLbl,0,wx.TOP,3)
        mySizer.Add((10,1))
        mySizer.Add(sepCtrl)
        while panel.Affirmed():
            panel.SetResult(
                sepCtrl.GetValue(),
                resCtrl.GetValue()
            )

    class text():
        tracks = '%s tracks'
        albums ='%s albums'
#====================================================================

class GetNotAccessibleTracks(eg.ActionBase):
    name = "Show/Edit inaccessible tracks"
    description = u'''<rst>**Show/Edit tracks that MediaMonkey cannot access.**

ATTENTION !!! This operation may take several minutes !!!
List of found tracks is exported to selected file.
For each track is recorded song ID, song title and file path.
You can this file import for example to MS Excel or OOo Calc'''

    frameIsOpen = False

    def __call__(self,path=""):
        if not path:
            path = eg.folderPath.Documents + '\\UnaccessibleTracks.txt'
        if not self.frameIsOpen:
            self.unaccessibleTracksFrame = UnaccessibleTracksFrame(parent = self)
            wx.CallAfter(
                self.unaccessibleTracksFrame.ShowUnaccessibleTracksFrame,
                path,
            )
            self.frameIsOpen = True

    def Configure(self, path=''):
        txt = self.text
        panel = eg.ConfigPanel(self)
        label1Text = wx.StaticText(panel, -1, self.text.label1)
        if not path:
            path = eg.folderPath.Documents + '\\UnaccessibleTracks.txt'
        self.path = path
        startFolder = path_split(path)[1]
        filepathCtrl = eg.FileBrowseButton(
            panel,
            size=(410,-1),
            toolTip = self.plugin.text.toolTipFolder,
            dialogTitle = self.plugin.text.browseTitle,
            buttonText = eg.text.General.browse,
            fileMask="CSV files (*.csv)|*.csv|"\
                "Text file (*.txt)|*.txt|"\
                "All files (*.*)|*.*",
        )
        filepathCtrl.SetValue(path)
        openButton = wx.Button(panel, -1, txt.openButton)
        openButton.SetToolTip(wx.ToolTip(txt.baloonBttn % path))
        sizerAdd = panel.sizer.Add
        sizerAdd(label1Text, 0, wx.TOP,15)
        sizerAdd(filepathCtrl,0,wx.TOP,3)
        sizerAdd(openButton,0,wx.TOP,30)

        def onOpenBtnClick(event):
            if not self.frameIsOpen:
                self.unaccessibleTracksFrame = UnaccessibleTracksFrame(parent = self)
                wx.CallAfter(
                    self.unaccessibleTracksFrame.ShowUnaccessibleTracksFrame,
                    filepathCtrl.GetValue(),
                    False
                )
                self.frameIsOpen = True
            event.Skip()

        openButton.Bind(wx.EVT_BUTTON, onOpenBtnClick)

        while panel.Affirmed():
            panel.SetResult(filepathCtrl.GetValue()
            )

    class text():
        label1 = "Target file for export:"
        openButton = "Open/create track list file"
        baloonBttn ='''Open dialog to edit file %s.
If this file does not exist, it will first be created.'''
#====================================================================

class GetSomeInfo(eg.ActionBase):
    def __call__(self):
        return self.value[0]*self.plugin.GetValue(self.value[1])
#====================================================================

class GetIsRunning(eg.ActionBase):
    def __call__(self):
        return self.plugin.GetIsRunning() == True
#====================================================================

class GetShuffle(eg.ActionBase):
    def __call__(self):
        val = self.plugin.GetValue('isShuffle')
        return val
#====================================================================

class GetStatus(eg.ActionBase):
    name = "Get Status"
    description = "Get Player Status (returns string: Playing, Paused or Stopped)."

    def __call__(self):
        playing=self.plugin.GetValue('isPlaying')
        paused=self.plugin.GetValue('isPaused')
        if not playing:
            return self.text.stopped
        elif playing and not paused:
            return self.text.playing
        elif playing and paused:
            return self.text.paused

    class text():
        playing = "Playing"
        paused = "Paused"
        stopped = "Stopped"
#====================================================================

class GetSongInfo(eg.ActionBase):

    def __call__(self, arrayInfo, sep = '', res = True):
        shuffle = self.plugin.GetValue('isShuffle')
        if not shuffle or self.value == 0:
            try:
                SongData, ix = self.plugin.GetSongData(self.value)
            except:
                return None
            if (SongData, ix) == (None, None):
                #return self.text.noData
                return None
            path=SongData['Path']
            indx=path.rfind("\\")+1
            result = []
            if arrayInfo[0]:
                result.append(path[:indx])
            if arrayInfo[1]:
                result.append(path[indx:])
            listPropert=(
                "Title",
                "ArtistName",
                "Genre",
                "Rating",
                "AlbumName",
                "DiscNumberStr",
                "TrackOrderStr",
                "AlbumArtistName",
                "Year",
                "Grouping",
                "OriginalYear",
                "Author",
                "Conductor",
                "Comment"
            )
            for propert, cond in zip(listPropert, arrayInfo[2:]):
                if cond:
                    result.append(SongData[propert])
            if arrayInfo[16]:
                result.append(str(ix))
            if res:
                if sep == '':
                    sep = '\n'
                return sep.join(result)
            else:
                return result
        else:
            return self.text.shuffleON


    def GetLabel(self, arrayInfo,sep=''):
        result=""
        for condition in arrayInfo:
            result+="X" if condition else "_"
        return result

    def Configure(
        self,
        arrayInfo=[False]*17,
        sep='',
        res = True
    ):
        text=dict(zip([item[0] for item in SONG_TABLE_FIELDS],Text.SongTableFields))
        panel = eg.ConfigPanel(self)
        filepathCtrl = wx.CheckBox(panel, -1, self.plugin.text.filepath)
        filepathCtrl.SetValue(arrayInfo[0])
        filenameCtrl = wx.CheckBox(panel, -1, self.plugin.text.filename)
        filenameCtrl.SetValue(arrayInfo[1])
        tracktitleCtrl = wx.CheckBox(panel, -1, text['SongTitle'])
        tracktitleCtrl.SetValue(arrayInfo[2])
        artistCtrl = wx.CheckBox(panel, -1, text['Artist'])
        artistCtrl.SetValue(arrayInfo[3])
        genreCtrl = wx.CheckBox(panel, -1, text['Genre'])
        genreCtrl.SetValue(arrayInfo[4])
        ratingCtrl = wx.CheckBox(panel, -1, text['Rating'])
        ratingCtrl.SetValue(arrayInfo[5])
        albumCtrl = wx.CheckBox(panel, -1, text['Album'])
        albumCtrl.SetValue(arrayInfo[6])
        discCtrl = wx.CheckBox(panel, -1, text['DiscNumber'])
        discCtrl.SetValue(arrayInfo[7])
        trackCtrl = wx.CheckBox(panel, -1, text['TrackNumber'])
        trackCtrl.SetValue(arrayInfo[8])
        albumartistCtrl = wx.CheckBox(panel, -1, text['AlbumArtist'])
        albumartistCtrl.SetValue(arrayInfo[9])
        yearCtrl = wx.CheckBox(panel, -1, text['Year'])
        yearCtrl.SetValue(arrayInfo[10])
        groupingCtrl = wx.CheckBox(panel, -1, text['GroupDesc'])
        groupingCtrl.SetValue(arrayInfo[11])
        origDateCtrl = wx.CheckBox(panel, -1, text['OrigYear'])
        origDateCtrl.SetValue(arrayInfo[12])
        composerCtrl = wx.CheckBox(panel, -1, text['Author'])
        composerCtrl.SetValue(arrayInfo[13])
        conductorCtrl = wx.CheckBox(panel, -1, text['Conductor'])
        conductorCtrl.SetValue(arrayInfo[14])
        commentCtrl = wx.CheckBox(panel, -1, text['Comment'])
        commentCtrl.SetValue(arrayInfo[15])
        seqNumCtrl = wx.CheckBox(panel, -1, self.text.seqNum)
        seqNumCtrl.SetValue(arrayInfo[16])
        resCtrl = wx.CheckBox(panel, -1, self.plugin.text.resType)
        resCtrl.SetValue(res)
        sepCtrl = wx.TextCtrl(panel,-1,sep)
        sepCtrl.SetToolTip(wx.ToolTip(self.plugin.text.sepToolTip))
        sepLabel = wx.StaticText(panel,-1,self.plugin.text.sepLabel)
        sepLabel.SetToolTip(wx.ToolTip(self.plugin.text.sepToolTip))

        def onResCtrl(evt = None):
            enable = resCtrl.GetValue()
            sepCtrl.Enable(enable)
            sepLabel.Enable(enable)
            if evt:
                evt.Skip()
        resCtrl.Bind(wx.EVT_CHECKBOX, onResCtrl)
        onResCtrl()

        line = wx.StaticLine(panel, -1, size=(440,-1), style=wx.LI_HORIZONTAL)
        panelSizer = panel.sizer
        bottomSizer = wx.BoxSizer(wx.HORIZONTAL)
        mainSizer=wx.FlexGridSizer(2,2)
        leftSizer=wx.BoxSizer(wx.VERTICAL)
        rightSizer=wx.BoxSizer(wx.VERTICAL)

        bottomSizer.Add(sepLabel,0,wx.TOP,4)
        bottomSizer.Add((10,1))
        bottomSizer.Add(sepCtrl,0)

        leftSizer.Add(tracktitleCtrl,0)
        leftSizer.Add(artistCtrl,0,wx.TOP,10)
        leftSizer.Add(genreCtrl,0,wx.TOP,10)
        leftSizer.Add(albumCtrl,0,wx.TOP,10)
        leftSizer.Add(albumartistCtrl,0,wx.TOP,10)
        leftSizer.Add(groupingCtrl,0,wx.TOP,10)
        leftSizer.Add(composerCtrl,0,wx.TOP,10)
        leftSizer.Add(conductorCtrl,0,wx.TOP,10)
        leftSizer.Add(seqNumCtrl,0,wx.TOP,10)
        rightSizer.Add(filepathCtrl,0)
        rightSizer.Add(filenameCtrl,0,wx.TOP,10)
        rightSizer.Add(yearCtrl,0,wx.TOP,10)
        rightSizer.Add(origDateCtrl,0,wx.TOP,10)
        rightSizer.Add(discCtrl,0,wx.TOP,10)
        rightSizer.Add(trackCtrl,0,wx.TOP,10)
        rightSizer.Add(ratingCtrl,0,wx.TOP,10)
        rightSizer.Add(commentCtrl,0,wx.TOP,10)
        mainSizer.Add((200,1))
        mainSizer.Add((200,1))
        mainSizer.Add(leftSizer,0)
        mainSizer.Add(rightSizer,0)
        panelSizer.Add(mainSizer,0)
        panelSizer.Add(line,0,wx.TOP,10)
        panelSizer.Add(resCtrl,0,wx.TOP,10)
        panelSizer.Add(bottomSizer,0,wx.TOP,6)

        while panel.Affirmed():
            arrayInfo=[
                filepathCtrl.GetValue(),
                filenameCtrl.GetValue(),
                tracktitleCtrl.GetValue(),
                artistCtrl.GetValue(),
                genreCtrl.GetValue(),
                ratingCtrl.GetValue(),
                albumCtrl.GetValue(),
                discCtrl.GetValue(),
                trackCtrl.GetValue(),
                albumartistCtrl.GetValue(),
                yearCtrl.GetValue(),
                groupingCtrl.GetValue(),
                origDateCtrl.GetValue(),
                composerCtrl.GetValue(),
                conductorCtrl.GetValue(),
                commentCtrl.GetValue(),
                seqNumCtrl.GetValue()
            ]
            panel.SetResult(
                arrayInfo,
                sepCtrl.GetValue(),
                resCtrl.GetValue()
            )

    class text:
        seqNum = 'Sequence number in "Now playing" window'
        shuffleON = 'Shuffle is ON !'
        noData = 'No data !'
#====================================================================

class GetDetailSongInfo(eg.ActionBase):
    name = "Get detailed song info"
    description = "Get detailed information about the currently playing song."

    def __call__(self, arrayInfo, sep='', res = True):
        SongData,ix = self.plugin.GetSongData(0)
        listPropert=(
        "Lyricist",
        "BPM",
        "InvolvedPeople",
        "OriginalArtist",
        "OriginalTitle",
        "OriginalLyricist",
        "ISRC",
        "Publisher",
        "Encoder",
        "Copyright"
        )
        if sep == '':
            sep = '\n'
        result=""
        for propert,cond in zip(listPropert,arrayInfo):
            result += SongData[propert]+sep if cond else ""
        return result[:-len(sep)]

    def GetLabel(self, arrayInfo, sep=''):
        result=""
        for condition in arrayInfo:
            result+="X" if condition else "_"
        return result

    def Configure(
        self,
        arrayInfo=[False]*10,
        sep='',
        res = True
    ):
        text=dict(zip([item[0] for item in SONG_TABLE_FIELDS],Text.SongTableFields))
        panel = eg.ConfigPanel(self)
        lyricistCtrl = wx.CheckBox(panel, -1, text['Lyricist'])
        lyricistCtrl.SetValue(arrayInfo[0])
        BPMCtrl = wx.CheckBox(panel, -1, text['BPM'])
        BPMCtrl.SetValue(arrayInfo[1])
        involvedpeopleCtrl = wx.CheckBox(panel, -1, text['InvolvedPeople'])
        involvedpeopleCtrl.SetValue(arrayInfo[2])
        originalartistCtrl = wx.CheckBox(panel, -1, text['OrigArtist'])
        originalartistCtrl.SetValue(arrayInfo[3])
        originaltitleCtrl = wx.CheckBox(panel, -1, text['OrigTitle'])
        originaltitleCtrl.SetValue(arrayInfo[4])
        originallyricistCtrl = wx.CheckBox(
            panel,
            -1,
            text['OrigLyricist']
        )
        originallyricistCtrl.SetValue(arrayInfo[5])
        ISRCCtrl = wx.CheckBox(panel, -1, text['ISRC'])
        ISRCCtrl.SetValue(arrayInfo[6])
        publisherCtrl = wx.CheckBox(panel, -1, text['Publisher'])
        publisherCtrl.SetValue(arrayInfo[7])
        encoderCtrl = wx.CheckBox(panel, -1, text['Encoder'])
        encoderCtrl.SetValue(arrayInfo[8])
        copyrightCtrl = wx.CheckBox(panel, -1, text['Copyright'])
        copyrightCtrl.SetValue(arrayInfo[9])
        sepCtrl = wx.TextCtrl(panel,-1,sep)
        sepCtrl.SetToolTip(wx.ToolTip(self.plugin.text.sepToolTip))
        sepLabel = wx.StaticText(panel,-1,self.plugin.text.sepLabel)
        sepLabel.SetToolTip(wx.ToolTip(self.plugin.text.sepToolTip))
        resCtrl = wx.CheckBox(panel, -1, self.plugin.text.resType)
        resCtrl.SetValue(res)


        def onResCtrl(evt = None):
            enable = resCtrl.GetValue()
            sepCtrl.Enable(enable)
            sepLabel.Enable(enable)
            if evt:
                evt.Skip()
        resCtrl.Bind(wx.EVT_CHECKBOX, onResCtrl)
        onResCtrl()

        line = wx.StaticLine(panel, -1, size=(440,-1), style=wx.LI_HORIZONTAL)

        panelSizer = panel.sizer
        bottomSizer = wx.BoxSizer(wx.HORIZONTAL)
        mainSizer=wx.FlexGridSizer(2,2)
        leftSizer=wx.BoxSizer(wx.VERTICAL)
        rightSizer=wx.BoxSizer(wx.VERTICAL)

        bottomSizer.Add(sepLabel,0,wx.TOP,4)
        bottomSizer.Add((10,1))
        bottomSizer.Add(sepCtrl,0)

        leftSizer.Add(lyricistCtrl,0)
        leftSizer.Add(involvedpeopleCtrl,0,wx.TOP,10)
        leftSizer.Add(originalartistCtrl,0,wx.TOP,10)
        leftSizer.Add(originaltitleCtrl,0,wx.TOP,10)
        leftSizer.Add(originallyricistCtrl,0,wx.TOP,10)
        rightSizer.Add(BPMCtrl,0)
        rightSizer.Add(ISRCCtrl,0,wx.TOP,10)
        rightSizer.Add(publisherCtrl,0,wx.TOP,10)
        rightSizer.Add(encoderCtrl,0,wx.TOP,10)
        rightSizer.Add(copyrightCtrl,0,wx.TOP,10)
        mainSizer.Add((200,1))
        mainSizer.Add((200,1))
        mainSizer.Add(leftSizer,0)
        mainSizer.Add(rightSizer,0)
        panelSizer.Add(mainSizer,0)
        panelSizer.Add(line,0,wx.TOP,10)
        panelSizer.Add(resCtrl,0,wx.TOP,10)
        panelSizer.Add(bottomSizer,0,wx.TOP,10)

        while panel.Affirmed():
            arrayInfo=[
                lyricistCtrl.GetValue(),
                BPMCtrl.GetValue(),
                involvedpeopleCtrl.GetValue(),
                originalartistCtrl.GetValue(),
                originaltitleCtrl.GetValue(),
                originallyricistCtrl.GetValue(),
                ISRCCtrl.GetValue(),
                publisherCtrl.GetValue(),
                encoderCtrl.GetValue(),
                copyrightCtrl.GetValue()
            ]
            panel.SetResult(
                arrayInfo,
                sepCtrl.GetValue(),
                resCtrl.GetValue()
            )
#====================================================================

class GetClassificationInfo(eg.ActionBase):
    name = "Get classification song info"
    description = "Get classification song information about the currently playing song."

    def __call__(self, arrayInfo, sep='', res = True):
        SongData,ix = self.plugin.GetSongData(0)
        listPropert=(
            "Tempo",
            "Mood",
            "Occasion",
            "Quality",
            "Custom1",
            "Custom2",
            "Custom3",
            "Custom4",
            "Custom5"
        )
        if sep == '':
            sep = '\n'
        result=""
        for propert,cond in zip(listPropert,arrayInfo):
            result+=SongData[propert]+sep if cond else ""
        return result[:-len(sep)]

    def GetLabel(self, arrayInfo, sep=''):
        result=""
        for condition in arrayInfo:
            result+="X" if condition else "_"
        return result

    def Configure(
        self,
        arrayInfo=[False]*9,
        sep='',
        res = True
    ):
        text=dict(zip([item[0] for item in SONG_TABLE_FIELDS],Text.SongTableFields))
        panel = eg.ConfigPanel(self)
        tempoCtrl = wx.CheckBox(panel, -1, text['Tempo'])
        tempoCtrl.SetValue(arrayInfo[0])
        moodCtrl = wx.CheckBox(panel, -1, text['Mood'])
        moodCtrl.SetValue(arrayInfo[1])
        occasionCtrl = wx.CheckBox(panel, -1, text['Occasion'])
        occasionCtrl.SetValue(arrayInfo[2])
        qualityCtrl = wx.CheckBox(panel, -1, text['Quality'])
        qualityCtrl.SetValue(arrayInfo[3])
        custom1Ctrl = wx.CheckBox(panel, -1, text['Custom1'])
        custom1Ctrl.SetValue(arrayInfo[4])
        custom2Ctrl = wx.CheckBox(panel, -1, text['Custom2'])
        custom2Ctrl.SetValue(arrayInfo[5])
        custom3Ctrl = wx.CheckBox(panel, -1, text['Custom3'])
        custom3Ctrl.SetValue(arrayInfo[6])
        custom4Ctrl = wx.CheckBox(panel, -1, text['Custom4'])
        custom4Ctrl.SetValue(arrayInfo[7])
        custom5Ctrl = wx.CheckBox(panel, -1, text['Custom5'])
        custom5Ctrl.SetValue(arrayInfo[8])
        sepCtrl = wx.TextCtrl(panel,-1,sep)
        sepCtrl.SetToolTip(wx.ToolTip(self.plugin.text.sepToolTip))
        sepLabel = wx.StaticText(panel,-1,self.plugin.text.sepLabel)
        sepLabel.SetToolTip(wx.ToolTip(self.plugin.text.sepToolTip))
        resCtrl = wx.CheckBox(panel, -1, self.plugin.text.resType)
        resCtrl.SetValue(res)


        def onResCtrl(evt = None):
            enable = resCtrl.GetValue()
            sepCtrl.Enable(enable)
            sepLabel.Enable(enable)
            if evt:
                evt.Skip()
        resCtrl.Bind(wx.EVT_CHECKBOX, onResCtrl)
        onResCtrl()

        line = wx.StaticLine(panel, -1, size=(440,-1), style=wx.LI_HORIZONTAL)
        panelSizer = panel.sizer
        bottomSizer = wx.BoxSizer(wx.HORIZONTAL)
        mainSizer=wx.FlexGridSizer(2,2)
        leftSizer=wx.BoxSizer(wx.VERTICAL)
        rightSizer=wx.BoxSizer(wx.VERTICAL)

        bottomSizer.Add(sepLabel,0,wx.TOP,4)
        bottomSizer.Add((10,1))
        bottomSizer.Add(sepCtrl,0)

        leftSizer.Add(tempoCtrl,0)
        leftSizer.Add(moodCtrl,0,wx.TOP,10)
        leftSizer.Add(occasionCtrl,0,wx.TOP,10)
        leftSizer.Add(qualityCtrl,0,wx.TOP,10)
        rightSizer.Add(custom1Ctrl,0)
        rightSizer.Add(custom2Ctrl,0,wx.TOP,10)
        rightSizer.Add(custom3Ctrl,0,wx.TOP,10)
        rightSizer.Add(custom4Ctrl,0,wx.TOP,10)
        rightSizer.Add(custom5Ctrl,0,wx.TOP,10)
        mainSizer.Add((200,1))
        mainSizer.Add((200,1))
        mainSizer.Add(leftSizer,0)
        mainSizer.Add(rightSizer,0)
        panelSizer.Add(mainSizer,0)
        panelSizer.Add(line,0,wx.TOP,10)
        panelSizer.Add(resCtrl,0,wx.TOP,10)
        panelSizer.Add(bottomSizer,0,wx.TOP,10)

        while panel.Affirmed():
            arrayInfo=[
                tempoCtrl.GetValue(),
                moodCtrl.GetValue(),
                occasionCtrl.GetValue(),
                qualityCtrl.GetValue(),
                custom1Ctrl.GetValue(),
                custom2Ctrl.GetValue(),
                custom3Ctrl.GetValue(),
                custom4Ctrl.GetValue(),
                custom5Ctrl.GetValue()
            ]
            panel.SetResult(
                arrayInfo,
                sepCtrl.GetValue(),
                resCtrl.GetValue()
            )
#====================================================================

class GetTechnicalSongInfo(eg.ActionBase):
    name = "Get technical song info"
    description = "Get technical information about the currently playing song. (song length, bitrate, etc.)"

    def __call__(self, arrayInfo, sep='',res = True):
        SongData,ix = self.plugin.GetSongData(0)
        listPropert=(
            "SongLength",
            "FileLength",
            "Bitrate",
            "VBR",
            "SampleRate",
            "Channels",
            "PlayCounter",
            "Leveling",
            "LastPlayed"
        )
        if sep == '':
            sep = '\n'
        result=""
        for propert,cond in zip(listPropert,arrayInfo):
            result+=SongData[propert]+sep if cond else ""
        return result[:-len(sep)]

    def GetLabel(self, arrayInfo, sep=''):
        result=""
        for condition in arrayInfo:
            result+="X" if condition else "_"
        return result

    def Configure(
        self,
        arrayInfo=[False]*9,
        sep='',
        res = True
    ):
        text=dict(zip([item[0] for item in SONG_TABLE_FIELDS],Text.SongTableFields))
        panel = eg.ConfigPanel(self)
        lengthCtrl = wx.CheckBox(panel, -1, text['SongLength'])
        lengthCtrl.SetValue(arrayInfo[0])
        filesizeCtrl = wx.CheckBox(panel, -1, text['FileLength'])
        filesizeCtrl.SetValue(arrayInfo[1])
        bitrateCtrl = wx.CheckBox(panel, -1, text['Bitrate'])
        bitrateCtrl.SetValue(arrayInfo[2])
        VBRCtrl = wx.CheckBox(panel, -1, text['VBR'])
        VBRCtrl.SetValue(arrayInfo[3])
        frequencyCtrl = wx.CheckBox(panel, -1, text['SamplingFrequency'])
        frequencyCtrl.SetValue(arrayInfo[4])
        stereoCtrl = wx.CheckBox(panel, -1, text['Stereo'])
        stereoCtrl.SetValue(arrayInfo[5])
        counterCtrl = wx.CheckBox(panel, -1, text['PlayCounter'])
        counterCtrl.SetValue(arrayInfo[6])
        levelingCtrl = wx.CheckBox(panel, -1, text['NormalizeTrack'])
        levelingCtrl.SetValue(arrayInfo[7])
        lastplayedCtrl = wx.CheckBox(panel, -1, text['LastTimePlayed'])
        lastplayedCtrl.SetValue(arrayInfo[8])
        sepCtrl = wx.TextCtrl(panel,-1,sep)
        sepCtrl.SetToolTip(wx.ToolTip(self.plugin.text.sepToolTip))
        sepLabel = wx.StaticText(panel,-1,self.plugin.text.sepLabel)
        sepLabel.SetToolTip(wx.ToolTip(self.plugin.text.sepToolTip))
        resCtrl = wx.CheckBox(panel, -1, self.plugin.text.resType)
        resCtrl.SetValue(res)


        def onResCtrl(evt = None):
            enable = resCtrl.GetValue()
            sepCtrl.Enable(enable)
            sepLabel.Enable(enable)
            if evt:
                evt.Skip()
        resCtrl.Bind(wx.EVT_CHECKBOX, onResCtrl)
        onResCtrl()

        line = wx.StaticLine(panel, -1, size=(440,-1), style=wx.LI_HORIZONTAL)
#        seekableCtrl = wx.CheckBox(panel, -1, text.seekable)
#        seekableCtrl.SetValue(seekable)
#        copyrightedCtrl = wx.CheckBox(panel, -1, text.copyrighted)
#        copyrightedCtrl.SetValue(copyrighted)
#        originalCtrl = wx.CheckBox(panel, -1, text.original)
#        originalCtrl.SetValue(original)
        panelSizer = panel.sizer
        bottomSizer = wx.BoxSizer(wx.HORIZONTAL)
        mainSizer=wx.FlexGridSizer(2,2)
        leftSizer=wx.BoxSizer(wx.VERTICAL)
        rightSizer=wx.BoxSizer(wx.VERTICAL)
        bottomSizer.Add(sepLabel,0,wx.TOP,4)
        bottomSizer.Add((10,1))
        bottomSizer.Add(sepCtrl,0)
        leftSizer.Add(lengthCtrl,0)
        leftSizer.Add(bitrateCtrl,0,wx.TOP,10)
        leftSizer.Add(frequencyCtrl,0,wx.TOP,10)
        leftSizer.Add(counterCtrl,0,wx.TOP,10)
        leftSizer.Add(lastplayedCtrl,0,wx.TOP,10)
        rightSizer.Add(filesizeCtrl,0)
        rightSizer.Add(VBRCtrl,0,wx.TOP,10)
        rightSizer.Add(stereoCtrl,0,wx.TOP,10)
        rightSizer.Add(levelingCtrl,0,wx.TOP,10)
        mainSizer.Add((200,1))
        mainSizer.Add((200,1))
        mainSizer.Add(leftSizer,0)
        mainSizer.Add(rightSizer,0)
        panelSizer.Add(mainSizer,0)
        panelSizer.Add(line,0,wx.TOP,10)
        panelSizer.Add(resCtrl,0,wx.TOP,10)
        panelSizer.Add(bottomSizer,0,wx.TOP,10)
#        panel.AddCtrl(seekableCtrl)
#        panel.AddCtrl(copyrightedCtrl)
#        panel.AddCtrl(originalCtrl)

        while panel.Affirmed():
            arrayInfo=[
                lengthCtrl.GetValue(),
                filesizeCtrl.GetValue(),
                bitrateCtrl.GetValue(),
                VBRCtrl.GetValue(),
                frequencyCtrl.GetValue(),
                stereoCtrl.GetValue(),
                counterCtrl.GetValue(),
                levelingCtrl.GetValue(),
                lastplayedCtrl.GetValue(),
#                seekableCtrl.GetValue(),
#                copyrightedCtrl.GetValue(),
#                originalCtrl.GetValue(),
            ]
            panel.SetResult(
                arrayInfo,
                sepCtrl.GetValue(),
                resCtrl.GetValue()
            )

    class text:
        length = "Length"
        filesize = "File size"
        bitrate = "Bitrate"
        VBR = "VBR"
        frequency = "Frequency"
        stereo = "Stereo"
        counter = "Play counter"
        leveling = "Leveling"
        lastplayed = "Last played"
#        seekable = "Seekable"
#        copyrighted = "Copyrighted"
#        original = "Original"
#====================================================================

class GetUniversal(eg.ActionBase):
    name = "Get Universal"
    description = "Get any one piece of information about the currently playing song."

    def __call__(self, field):
        SongData,ix = self.plugin.GetSongData(0)
        tmpList = [tpl[2] for tpl in SONG_TABLE_FIELDS]
        return SongData[tmpList[field]]

    def GetLabel(self, field):
        return self.text.get % Text.SongTableFields[field]

    def Configure(self, field=-1):
        choices = filter(lambda i: i[0]!="",zip(
            [tpl[2] for tpl in SONG_TABLE_FIELDS],
            Text.SongTableFields
        ))
        choices = [item[1] for item in choices]
        choices.sort()
        panel=eg.ConfigPanel(self)
        panel.AddLabel(self.text.label)
        infoCtrl=wx.Choice(
            panel,
            choices=choices,
        )
        if field > -1:
            infoCtrl.SetStringSelection(Text.SongTableFields[field])
        panel.AddCtrl(infoCtrl)

        while panel.Affirmed():
            panel.SetResult(Text.SongTableFields.index(infoCtrl.GetStringSelection()))

    class text:
        label="Select requested property:"
        get = "Get %s"
#====================================================================

class WritingToMM(eg.ActionBase):
    name = "Write to MM database"
    description = "Write specific tags (mood etc) to currently playing song."

    def __init__(self):
        text=self.text
        self.listCtrl=(
            "wx.TextCtrl(panel, -1, value)",
            (
                "eg.SpinNumCtrl(panel,-1,value,max=100.0,min=0.0,"
                "fractionWidth=1,increment=10,style=wx.TE_READONLY)"
            )
        )
        self.propertiesList=(
            ("Tempo",0,False),
            ("Mood",0,False),
            ("Occasion",0,False),
            ("Quality",0,False),
            ("Custom1",0,False),
            ("Custom2",0,False),
            ("Custom3",0,False),
            ("Custom4",0,False),
            ("Custom5",0,False),
            ("Comment",0,True),
            ("Genre",0,True),
            ("Rating",1,True),
        )

    def __call__(self, i, arrayValue0, arrayValue1):
        ndx = [itm[0] for itm in self.propertiesList].index([it[0] for it in SONG_TABLE_FIELDS][i])
        tmpList=[]
        attrib = [itm[2] for itm in SONG_TABLE_FIELDS][i]
        if self.plugin.workerThread:
            self.plugin.workerThread.CallWait(partial(
                self.plugin.workerThread.WriteToMMdatabase,
                attrib,
                arrayValue0[ndx],
                arrayValue1[ndx]
            ))

    def GetLabel(self, i, arrayValue0, arrayValue1):
        ndx = [itm[0] for itm in self.propertiesList].index([it[0] for it in SONG_TABLE_FIELDS][i])
        lbl = Text.SongTableFields[i]
        itm2 = arrayValue0[ndx] if (self.propertiesList[ndx][1] == 0) else str(int(arrayValue0[ndx]))
        result = self.text.set % (lbl, itm2)
        if arrayValue1[ndx]:
            result += " (+ID3)"
        return result

    def Configure(
        self,
        i=-1,
        arrayValue0=[
            "",
            "",
            "",
            "",
            "",
            "",
            "",
            "",
            "",
            "",
            "",
            50,
        ],
        arrayValue1 = [False] * 12
    ):
        arrayVal0 = arrayValue0[:]
        arrayVal1 = arrayValue1[:]
        choices=[]
        for item in self.propertiesList:
            indx = [ix[0] for ix in SONG_TABLE_FIELDS].index(item[0])
            choices.append(Text.SongTableFields[indx])
        choices.sort()
        panel = eg.ConfigPanel(self)
        choiceLbl=wx.StaticText(panel, -1, self.text.label)
        choiceCtrl=wx.Choice(
            panel,
            choices=choices,
        )
        choiceCtrl.SetStringSelection(Text.SongTableFields[i])
        mainSizer =wx.BoxSizer(wx.VERTICAL)
        choiceSizer = wx.GridSizer(rows=1, cols=2, hgap=5, vgap=5)
        choiceSizer.Add(choiceLbl, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
        choiceSizer.Add(choiceCtrl, 0, wx.EXPAND)
        mainSizer.Add(choiceSizer, 0, wx.EXPAND|wx.ALL, 10)
        dynSizer = wx.GridSizer(rows=1, cols=2, hgap=5, vgap=5)
        mainSizer.Add(dynSizer, 0, wx.EXPAND|wx.BOTTOM, 10)
        panel.sizer.Add(mainSizer)

        def onChoiceChange(event=None):
            dynSizer.Clear(True)
            dynLbl = wx.StaticText(
                panel,
                -1,
                choiceCtrl.GetStringSelection()+":"
            )
            ix = Text.SongTableFields.index(choiceCtrl.GetStringSelection())
            field = SONG_TABLE_FIELDS[ix][0]
            ndx = [item[0] for item in self.propertiesList].index(field)
            line = self.propertiesList[ndx][1]
            value = arrayVal0[ndx]
            dynCtrl = eval(self.listCtrl[line])
            dynSizer.Add(dynLbl, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
            dynSizer.Add(dynCtrl, 0, wx.EXPAND)
            if self.propertiesList[ndx][2]:
                chkBoxCtrl = wx.CheckBox(panel, label=self.text.checkboxlabel)
                chkBoxCtrl.SetValue(arrayVal1[ndx])
                dynSizer.Add((5,5))
                dynSizer.Add(chkBoxCtrl, 0, wx.EXPAND)
            mainSizer.Layout()
            if event:
                event.Skip()

        choiceCtrl.Bind(wx.EVT_CHOICE, onChoiceChange)
        if i> -1:
            ndx = [itm[0] for itm in self.propertiesList].index([it[0] for it in SONG_TABLE_FIELDS][i])
            onChoiceChange()

        while panel.Affirmed():
            ix = Text.SongTableFields.index(choiceCtrl.GetStringSelection())
            field = SONG_TABLE_FIELDS[ix][0]
            ndx = [item[0] for item in self.propertiesList].index(field)
            arrayVal0[ndx] = dynSizer.GetChildren()[1].GetWindow().GetValue()
            if self.propertiesList[ndx][2]:
                arrayVal1[ndx] = dynSizer.GetChildren()[3].GetWindow().GetValue()

            panel.SetResult(ix, arrayVal0, arrayVal1 )

    class text:
        label="Select requested property:"
        set = "Set %s = %s"
        checkboxlabel = "Make entry to ID3 tag too"
#====================================================================

class LoadPlaylist(eg.ActionBase):
    name = "Load Playlist by Name"
    description = "Loads a MediaMonkey playlist defined by name."

    def __call__(self, plString, repeat, shuffle, crossfade, clear):
        plString = eg.ParseString(plString)
        if not self.plugin.workerThread2:
            self.plugin.workerThread2 = MediaMonkeyWorkerThread(self.plugin)
            self.plugin.workerThread2.Start(100.0)
        args = (
            self.plugin.workerThread2.LoadPlaylistByTitle,
            plString,
            repeat,
            shuffle,
            crossfade,
            clear
        )
        self.plugin.workerThread2.Call(partial(*args))

    def Configure(self, plString="",repeat=2,shuffle=2,crossfade=2, clear = True):
        panel = eg.ConfigPanel(self)
        text = self.text
        textCtrl = wx.TextCtrl(panel, -1, plString)
        repeatChkBoxCtrl = wx.CheckBox(panel, label=Text.repeat,style=wx.CHK_3STATE|wx.CHK_ALLOW_3RD_STATE_FOR_USER)
        repeatChkBoxCtrl.Set3StateValue(repeat)
        shuffleChkBoxCtrl = wx.CheckBox(panel, label=Text.shuffle,style=wx.CHK_3STATE|wx.CHK_ALLOW_3RD_STATE_FOR_USER)
        shuffleChkBoxCtrl.Set3StateValue(shuffle)
        crossfadeChkBoxCtrl = wx.CheckBox(panel, label=Text.crossfade,style=wx.CHK_3STATE|wx.CHK_ALLOW_3RD_STATE_FOR_USER)
        crossfadeChkBoxCtrl.Set3StateValue(crossfade)
        clearPlaylistChkBoxCtrl = wx.CheckBox(panel, label=Text.clearPlaylist)
        clearPlaylistChkBoxCtrl.SetValue(clear)
        SizerAdd = panel.sizer.Add
        SizerAdd(wx.StaticText(panel, -1, text.playlistName))
        SizerAdd(textCtrl, 0, wx.EXPAND)
        leftSizer = wx.BoxSizer(wx.VERTICAL)
        bottmSizer = wx.BoxSizer(wx.HORIZONTAL)
        bottmSizer.Add(leftSizer,2,wx.EXPAND)
        SizerAdd(bottmSizer,0,wx.EXPAND)
        leftSizer.Add(repeatChkBoxCtrl,0,wx.TOP,15,)
        leftSizer.Add(shuffleChkBoxCtrl,0,wx.TOP,15)
        leftSizer.Add(crossfadeChkBoxCtrl,0,wx.TOP,15)
        leftSizer.Add(clearPlaylistChkBoxCtrl,0,wx.TOP,15)

        while panel.Affirmed():
            panel.SetResult(
                textCtrl.GetValue(),
                repeatChkBoxCtrl.Get3StateValue(),
                shuffleChkBoxCtrl.Get3StateValue(),
                crossfadeChkBoxCtrl.Get3StateValue(),
                clearPlaylistChkBoxCtrl.GetValue(),
            )

    class text:
        playlistName = "Playlist name:"
        found = "found (%s songs)"
        noFound = "not found or empty"
#====================================================================

class AddCurrentSongToPlaylist(eg.ActionBase):
    name = "Add currently playing song to playlist"
    description = "Adds the currently playing song to a specific playlist."

    def __call__(self, plString, skip):
        if self.plugin.workerThread:
            args = (
                self.plugin.workerThread.AddSongToPlaylist,
                plString,
                skip
            )
            self.plugin.workerThread.Call(partial(*args))

    def Configure(self, plString="", skip=False):
        panel = eg.ConfigPanel(self)
        text = self.text
        textCtrl = wx.TextCtrl(panel, -1, plString)
        SizerAdd = panel.sizer.Add
        SizerAdd(wx.StaticText(panel, -1, text.playlistName))
        SizerAdd(textCtrl, 0, wx.EXPAND)
        skipChkBoxCtrl = wx.CheckBox(panel, label=self.text.skip)
        skipChkBoxCtrl.SetValue(skip)
        SizerAdd(skipChkBoxCtrl,0,wx.TOP,15,)

        while panel.Affirmed():
            panel.SetResult(
                textCtrl.GetValue(),
                skipChkBoxCtrl.GetValue(),
            )

    class text:
        playlistName = "Playlist name:"
        skip = "Skip to next track"
#====================================================================

class RemoveCurrentSongFromPlaylist(eg.ActionBase):
    name = "Remove current playing song from Playlist"
    description = "Remove the currently playing song from a specific playlist."

    def __call__(self, plString, skip, now_pl):
        if self.plugin.workerThread:
            args = (
                self.plugin.workerThread.RemoveSongFromPlaylist,
                plString,
                skip,
                now_pl
            )
            self.plugin.workerThread.Call(partial(*args))

    def Configure(self, plString="", skip=False, now_pl=False):
        panel = eg.ConfigPanel(self)
        text = self.text
        textCtrl = wx.TextCtrl(panel, -1, plString)
        SizerAdd = panel.sizer.Add
        SizerAdd(wx.StaticText(panel, -1, text.playlistName))
        SizerAdd(textCtrl, 0, wx.EXPAND)
        skipChkBoxCtrl = wx.CheckBox(panel, label=self.text.skip)
        skipChkBoxCtrl.SetValue(skip)
        SizerAdd(skipChkBoxCtrl,0,wx.TOP,15,)
        now_plChkBoxCtrl = wx.CheckBox(panel, label=self.text.now_pl)
        now_plChkBoxCtrl.SetValue(now_pl)
        SizerAdd(now_plChkBoxCtrl,0,wx.TOP,15,)

        while panel.Affirmed():
            panel.SetResult(
                textCtrl.GetValue(),
                skipChkBoxCtrl.GetValue(),
                now_plChkBoxCtrl.GetValue(),
            )

    class text:
        playlistName = "Playlist name:"
        skip = "Skip to next track"
        now_pl='Remove track from "Now playing" window too'
#====================================================================

class RemoveCurrentSongFromNowPlaying(eg.ActionBase):
    name = "Remove currently playing song from Now Playing playlist"
    description = "Remove the currently playing song from Now Playing playlist."

    def __call__(self, skip):
        if self.plugin.workerThread:
            self.plugin.workerThread.Call(partial(
                    self.plugin.workerThread.RemoveSongFromNowPlaying,
                    skip
                ))

    def Configure(self, skip=False):
        panel = eg.ConfigPanel(self)
        text = self.text
        SizerAdd = panel.sizer.Add
        skipChkBoxCtrl = wx.CheckBox(panel, label=self.text.skip)
        skipChkBoxCtrl.SetValue(skip)
        SizerAdd(skipChkBoxCtrl,0,wx.TOP,15,)

        while panel.Affirmed():
            panel.SetResult(
                skipChkBoxCtrl.GetValue(),
            )

    class text:
        skip = "Skip to next track"
#====================================================================

class LoadPlaylistByFilter(eg.ActionBase):
    name = "Load Playlist by Filter"
    description = "Loads a MediaMonkey playlist defined by filter (SQL query)."

    def __init__(self):
        self.myDirty=None

        self.unitList=(
           # "seconds",
            "minutes",
            "hours",
            "days",
            "months",
            "years"
        )

        self.trendList=(
            "asc",
            "desc"
        )

        self.exprList1=(
            "equal",
            "notEqual",
            "greater",
            "greatOrEqual",
            "less",
            "lowerOrEqual",
            "beforeLess",
            "beforeMore",
        )

        self.exprList=[
            "equal",
            "notEqual",
            "greater",
            "greatOrEqual",
            "less",
            "lowerOrEqual",
            "startsWith",
            "notStartsWith",
            "endsWith",
            "notEndsWith",
            "includes",
            "notIncludes",
            "isEmpty",
            "isNotEmpty",
        ]

    def __call__(
        self,
        plName,
        mode,
        listRules,
        order,
        trend,
        crit,
        limit,
        random,
        num,
        repeat,
        shuffle,
        crossfade,
        clear,
    ):
        if not self.plugin.workerThread2:
            self.plugin.workerThread2 = MediaMonkeyWorkerThread(self.plugin)
            self.plugin.workerThread2.Start(100.0)
        args = (
            self.plugin.workerThread2.LoadFilterPlaylist,
            plName,
            mode,
            listRules,
            order,
            trend,
            crit,
            limit,
            random,
            num,
            repeat,
            shuffle,
            crossfade,
            clear,
            self.unitList,
            self.trendList
  )
        self.plugin.workerThread2.Call(partial(*args))

    def Configure(
        self,
        plName="",
        mode=0,
        listRules=[[-1,-1,u""]],
        order=False,
        trend=0,
        crit=0,
        limit=False,
        random=False,
        num="100",
        repeat=2,
        shuffle=2,
        crossfade=2,
        clear=True,
    ):
        text = self.text
        def validityCheck():
            if CheckEnable:
                flag=True
                for i in range(0,self.i):
                    choice0=listRules2[i][0]
                    choice1=listRules2[i][1]
                    if choice0<0 or choice1<0:
                        flag=False
                        break
                    else:
                        if SONG_TABLE_FIELDS[choice0][1] <> "D":
                            if choice1 < 12 and listRules2[i][2]==u"":
                                flag=False
                                break
                        else:
                            if choice1>5 and int(listRules2 [i][2][:-1])<1:
                                flag=False
                                break
                if not self.myDirty:
                    panel.SetIsDirty(True)
                    myDirty=True
                panel.EnableButtons(flag)

        choices = list(Text.SongTableFields)
        choicesS=choices[:]
        choicesS.sort()

        listRules2 = [] #working copy (after Cancel flush it)
        for i in range(0,len(listRules)):
            listRules2.append(listRules[i][:])
        maxRules=10
        panel = eg.ConfigPanel(self)
        panel.sizer.SetMinSize((560, 110+29*maxRules))
        radioBoxMode = wx.RadioBox(
            panel,
            -1,
            text.radioboxMode,
            choices=[text.modeAnd, text.modeOr],
            style=wx.RA_SPECIFY_COLS
        )
        radioBoxMode.SetMinSize((556,43))
        radioBoxMode.SetSelection(mode)
        nameCtrl = wx.TextCtrl(panel, -1, plName,size=(100,22))
        orderChkBoxCtrl = wx.CheckBox(panel, label="")
        orderChkBoxCtrl.SetValue(order)
        dirTxt1=wx.StaticText(panel, -1, self.text.order1)
        trends=[eval("self.text."+tpl) for tpl in self.trendList]
        dirCtrl=wx.Choice(panel, -1, choices=trends,size=(-1, -1))
        dirCtrl.SetSelection(trend)
        dirTxt2=wx.StaticText(panel, -1, self.text.order2)
        critCtrl=wx.Choice(panel, -1, choices=choicesS,size=(-1, -1))
        critCtrl.SetStringSelection(choices[crit])
        limitChkBoxCtrl = wx.CheckBox(panel, label="")
        limitChkBoxCtrl.SetValue(limit)
        limitTxt1=wx.StaticText(panel, -1, self.text.limit1)
        numCtrl = masked.NumCtrl(
            panel, -1, num,
            min=1,integerWidth=6,
            allowNegative=False,groupDigits=False)
        limitTxt2=wx.StaticText(panel, -1, self.text.limit2+10*' ')
        randomChkBoxCtrl = wx.CheckBox(panel,  label=Text.random)
        randomChkBoxCtrl.SetToolTip(wx.ToolTip(Text.randomToolTip % Text.shuffle))
        randomChkBoxCtrl.SetValue(random)
        repeatChkBoxCtrl = wx.CheckBox(panel, label=Text.repeat,style=wx.CHK_3STATE|wx.CHK_ALLOW_3RD_STATE_FOR_USER)
        repeatChkBoxCtrl.Set3StateValue(repeat)
        shuffleChkBoxCtrl = wx.CheckBox(panel, label=Text.shuffle,style=wx.CHK_3STATE|wx.CHK_ALLOW_3RD_STATE_FOR_USER)
        shuffleChkBoxCtrl.Set3StateValue(shuffle)
        crossfadeChkBoxCtrl = wx.CheckBox(panel, label=Text.crossfade,style=wx.CHK_3STATE|wx.CHK_ALLOW_3RD_STATE_FOR_USER)
        crossfadeChkBoxCtrl.Set3StateValue(crossfade)
        clearPlaylistChkBoxCtrl = wx.CheckBox(panel, label=Text.clearPlaylist)
        clearPlaylistChkBoxCtrl.SetValue(clear)

        self.mySizer = wx.GridBagSizer(vgap=8,hgap=10)
        self.mySizer.SetMinSize((560, 6+29*maxRules))
        statBox_1 = wx.StaticBox(panel, -1, "")
        statBox_2 = wx.StaticBox(panel, -1, "")
        stBoxSizer = wx.StaticBoxSizer(statBox_1, wx.VERTICAL)
        stBoxSizer.SetMinSize((426,-1))
        rightSizer = wx.StaticBoxSizer(statBox_2, wx.VERTICAL)
        rightSizer.SetMinSize((120,69))
        orderSizer=wx.BoxSizer(wx.HORIZONTAL)
        limitSizer=wx.BoxSizer(wx.HORIZONTAL)
        leftSizer=wx.BoxSizer(wx.VERTICAL)
        lrSizer =wx.BoxSizer(wx.HORIZONTAL)
        bottmSizer = wx.FlexGridSizer(rows=2, cols=2, vgap=10, hgap=40)

        rightSizer.Add(wx.StaticText(panel, -1, text.filterName),0,wx.LEFT|wx.TOP,4)
        rightSizer.Add(nameCtrl, 0,wx.LEFT|wx.TOP,4)
        rightSizer.Add((1,12))
        orderSizer.Add(orderChkBoxCtrl,0,wx.TOP,4)
        orderSizer.Add(dirTxt1,0,wx.LEFT|wx.TOP,4)
        orderSizer.Add(dirCtrl,0,wx.LEFT,4)
        orderSizer.Add(dirTxt2,0,wx.LEFT|wx.TOP,4)
        orderSizer.Add(critCtrl,0,wx.LEFT,4)
        limitSizer.Add(limitChkBoxCtrl,0,wx.TOP,4)
        limitSizer.Add(limitTxt1,0,wx.LEFT|wx.TOP,4)
        limitSizer.Add(numCtrl, 0,wx.LEFT,4)
        limitSizer.Add(limitTxt2,0,wx.LEFT|wx.TOP,4)
        limitSizer.Add(randomChkBoxCtrl,0,wx.TOP,4)
        stBoxSizer.Add(limitSizer,0,wx.TOP,8)
        stBoxSizer.Add(orderSizer,0,wx.TOP,4)
        bottmSizer.Add(repeatChkBoxCtrl,0,wx.LEFT,5)
        bottmSizer.Add(shuffleChkBoxCtrl,0)
        bottmSizer.Add(crossfadeChkBoxCtrl,0,wx.LEFT,5)
        bottmSizer.Add(clearPlaylistChkBoxCtrl,0)
        leftSizer.Add(stBoxSizer,0)
        #leftSizer.Add(bottmSizer,0,wx.TOP,10)
        lrSizer.Add(leftSizer)
        lrSizer.Add(rightSizer,0,wx.LEFT,10)
        panel.sizer.Add(radioBoxMode, 0)
        panel.sizer.Add(self.mySizer, 0,wx.TOP,10)
        panel.sizer.Add(lrSizer)
        panel.sizer.Add(bottmSizer,0,wx.TOP,10)

        def CreateExprCtrl(row):
#Called from:     AddRow,UpdateChoiceExpr
            if SONG_TABLE_FIELDS[listRules2[row][0]][1]=="D": # Date & Time
                choicExpr=[eval("self.text."+tpl) for tpl in self.exprList1]
            else:
                choicExpr=[eval("self.text."+tpl) for tpl in self.exprList]
            exprCtrl=wx.Choice(panel, row+100, choices=choicExpr,size=(182, 22))
            exprCtrl.SetSelection(listRules2[row][1])
            exprCtrl.Bind(wx.EVT_CHOICE, OnExprChoice)
            self.mySizer.Add(exprCtrl,(row,1))
            self.mySizer.Layout()

        def ConvToWxDt(dt):
#Called from: CreateStrCtrl, UpdateStr
            """Conversion of data record to wx.DateTime format."""
            wxDttm=wx.DateTime()
            wxDttm.Set(int(dt[8:10]),int(dt[5:7])-1,int(dt[:4]))
            return wxDttm

        def CreateStrCtrl(row):
#Called from: UpdateStr, AddRow
            tp=SONG_TABLE_FIELDS[listRules2[row][0]][1]
            if tp<>"D":
                strCtrl=wx.TextCtrl(panel, row+150, "",size=(168, 22))
                strCtrl.Bind(wx.EVT_TEXT, OnStrChange)
                strCtrl.SetValue(listRules2[row][2])
                if listRules2[row][1]>11:
                    strCtrl.Enable(False)
                infoSizer=wx.BoxSizer(wx.HORIZONTAL)
                infoSizer.Add(strCtrl)
            else: # Date & Time Ctrl
                if listRules2[row][1]<6: #for absolute date/time type
                    clndrCtrl=wx.DatePickerCtrl(panel,row+150, size=(85,22),
                        style=wx.DP_DROPDOWN | wx.DP_SHOWCENTURY)
                    clndrCtrl.SetRange(ConvToWxDt('1900-01-01'),ConvToWxDt('2050-12-31'))
                    clndrCtrl.SetValue(ConvToWxDt(listRules2[row][2]))
                    clndrCtrl.Bind(wx.EVT_DATE_CHANGED, OnClndrChange)
                    infoSizer=wx.BoxSizer(wx.HORIZONTAL)
                    infoSizer.Add(clndrCtrl)
                    spinBtn = wx.SpinButton(panel,row+250, wx.DefaultPosition, (-1,22), wx.SP_VERTICAL )
                    timeCtrl = masked.TimeCtrl(
                        panel, row+200, name="24hrCtrl", fmt24hr=True,
                        spinButton=spinBtn
                        )
                    timeCtrl.SetValue(listRules2[row][2][11:])
                    timeCtrl.Bind(masked.EVT_TIMEUPDATE, OnTimeChange )
                    infoSizer.Add(timeCtrl,0,wx.LEFT,2)
                    infoSizer.Add(spinBtn)
                else: #for time  relative (NOW) type
                    periodCtrl = masked.NumCtrl(
                        panel, row+150, num,
                        size=(85,22),min=1,integerWidth=9,
                        allowNegative=False,groupDigits=False,
                        autoSize=False,invalidBackgroundColour = "White",)
                    #periodCtrl.Bind(masked.EVT_NUM, OnPeriodChange)
                    periodCtrl.Bind(wx.EVT_TEXT, OnPeriodChange) #Otherwise problem with Dirty flag !
                    periodCtrl.SetValue(int(listRules2[row][2][:-1]))
                    infoSizer=wx.BoxSizer(wx.HORIZONTAL)
                    infoSizer.Add(periodCtrl)
                    choicUnit=[eval("self.text."+tpl) for tpl in self.unitList]
                    unitCtrl=wx.Choice(panel, row+200, choices=choicUnit,size=(81, 22))
                    unitCtrl.SetSelection(int(listRules2[row][2][-1]))
                    unitCtrl.Bind(wx.EVT_CHOICE, OnUnitChoice)
                    infoSizer.Add(unitCtrl,0,wx.LEFT,2)
            self.mySizer.Add(infoSizer,(row,2))
            self.mySizer.Layout()

        def AddRow(x):
#Called from: OnAddButton, Main
            propertCtrl=wx.Choice(panel,x+50 , choices=choicesS,size=(132, 22))
            propertCtrl.Bind(wx.EVT_CHOICE, OnPropertChoice)
            self.mySizer.Add(propertCtrl,(x,0))
            CreateExprCtrl(x)
            CreateStrCtrl(x)
            btnAdd = wx.Button(panel, x, "+",size=(22, 22))
            btnAdd.Bind(wx.EVT_BUTTON,OnAddButton)
            btnRemove = wx.Button(panel, x, "-",size=(22, 22))
            btnRemove.Bind(wx.EVT_BUTTON,OnRemoveButton)
            self.mySizer.Add(btnAdd,(x,3))
            self.mySizer.Add(btnRemove,(x,4),flag=wx.LEFT,border=-10)
            self.mySizer.Layout()

        def UpdateChoiceExpr(row):
#Called from: updateRow, OnPropertChoice
            cnt=self.mySizer.FindItemAtPosition((row,1)).GetWindow().GetCount()
            tp=SONG_TABLE_FIELDS[listRules2[row][0]][1]
            myWnd=wx.FindWindowById(row+100)
            if cnt==0 or (tp=="D" and cnt==14) or (tp<>"D" and cnt<>14):
                self.mySizer.Detach(myWnd)
                myWnd.Destroy()
                CreateExprCtrl(row)
            else:
                myWnd.SetSelection(listRules2[row][1])

        def UpdateStr(row):
#Called from: updateRow, OnPropertChoice, OnExprChoice
            infoSizer=self.mySizer.FindItemAtPosition((row,2)).GetSizer()
            lng=len(infoSizer.GetChildren()) # old column 2 type markant
            tp=SONG_TABLE_FIELDS[listRules2[row][0]][1]
            tp2=listRules2[row][1]
            flag=False
            #First: Destroy old Ctrl(s)
            if tp=="D":
                if lng==1:
                    rng=(0,)
                    flag=True
                elif lng==2 and tp2<6 :
                    rng=(1,0)
                    flag=True
                elif lng==3 and tp2>5:
                    rng=(2,1,0)
                    flag=True
            else: #tp<>"D"
                if lng==2:
                    rng=(1,0,)
                    flag=True
                elif lng==3:
                    rng=(2,1,0)
                    flag=True
            if flag: #  update panel and value
                for indx in rng:
                    wnd=infoSizer.GetChildren()[indx].GetWindow()
                    infoSizer.Detach(wnd)
                    wnd.Destroy()
                self.mySizer.Detach(infoSizer)
                infoSizer.Destroy()
                #Second: Create new Ctrl(s)
                CreateStrCtrl(row) #Create and update value
            else: # update only value
                val=listRules2[row][2]
                wnd1=wx.FindWindowById(row+150)
                wnd2=wx.FindWindowById(row+200)
                if lng==1:
                    wnd1.SetValue(val)
                    if tp2>11:
                        wnd1.Enable(False)
                    else:
                        wnd1.Enable(True)
                else:
                    if tp2<6: # absolute date/time
                        wnd1.SetValue(ConvToWxDt(val))
                        wnd2.SetValue(val[11:])
                    else: # relative date/time
                        wnd1.SetValue(int(val[:-1]))
                        wnd2.SetSelection(int(val[-1]))

        def updateRow(row):
#Called from: OnAddButton, OnRemoveButton, Main
            if listRules2[row][0] > -1:
                wx.FindWindowById(row+50).SetStringSelection(choices[listRules2[row][0]])
            UpdateChoiceExpr(row)
            UpdateStr(row)

        def OnPropertChoice(evt):
            row=evt.GetId()-50
            field = wx.FindWindowById(evt.GetId()).GetStringSelection()
            listRules2[row][0]=choices.index(field)
            infoSizer=self.mySizer.FindItemAtPosition((row,2)).GetSizer()
            lng=len(infoSizer.GetChildren()) # old column 2 type markant
            tp=SONG_TABLE_FIELDS[listRules2[row][0]][1]
            cnt=self.mySizer.FindItemAtPosition((row,1)).GetWindow().GetCount()
            flg=False
            if tp=="D" and cnt==14: # change to absolute date/time format
                listRules2[row][2]=str(datetime.datetime.today())[:11]+'00:00:00'
                flg=True
            elif tp<>"D" and cnt<>14: #change to no date/time (single column) format
                listRules2[row][2]=""
                flg=True
            if flg: # set selection Expr to "no selection"
                listRules2[row][1]=-1
                wx.FindWindowById(row+100).SetSelection(-1)
            UpdateChoiceExpr(row)
            UpdateStr(row)
            validityCheck()

        def OnExprChoice(evt):
            row=evt.GetId()-100
            value=wx.FindWindowById(evt.GetId()).GetSelection() #
            listRules2[row][1]=value
            #enable=False if value>11 else True
            wnd=wx.FindWindowById(row+150)
            if SONG_TABLE_FIELDS[listRules2[row][0]][1]<>"D":
                if value>11:
                    wnd.Enable(False)
                    wnd.Clear()
                else:
                    wnd.Enable(True)
            else: # date/time format
                infoSizer=self.mySizer.FindItemAtPosition((row,2)).GetSizer()
                lng=len(infoSizer.GetChildren())
                if lng==2 and value<6:
                    listRules2[row][2]=str(datetime.datetime.today())[:11]+'00:00:00'
                elif lng==3 and value>5:
                    listRules2[row][2]='13'
                UpdateStr(row)
            validityCheck()

        def OnStrChange(evt):
            row=evt.GetId()-150
            listRules2[row][2]=wx.FindWindowById(evt.GetId()).GetValue()
            validityCheck()

        def OnClndrChange(evt):
            """Event handler for date change."""
            row=evt.GetId()-150
            dt=wx.FindWindowById(evt.GetId()).GetValue()
            listRules2[row][2]=time.strftime('%Y-%m-%d',time.strptime(str(dt),'%d.%m.%Y %H:%M:%S'))+' '+listRules2[row][2][11:]
            validityCheck()

        def OnTimeChange(evt):
            """Event handler for time change."""
            row=evt.GetId()-200
            listRules2[row][2]=listRules2[row][2][:11]+wx.FindWindowById(evt.GetId()).GetValue()
            validityCheck()

        def OnPeriodChange(evt):
            row=evt.GetId()-150
            wnd=wx.FindWindowById(evt.GetId())
            oldVal=listRules2[row][2]
            newVal=wnd.GetValue()
            listRules2[row][2]=str(newVal)+oldVal[-1]
            validityCheck()

        def OnUnitChoice(evt):
            row=evt.GetId()-200
            val=listRules2[row][2]
            listRules2[row][2]=val[:-1]+str(wx.FindWindowById(evt.GetId()).GetSelection())
            validityCheck()

        def OnAddButton(evt):
            """Event handler for the button '+' click."""
            if self.i<maxRules:
                #Insert new record at requested position
                listRules2.insert(evt.GetId()+1,[-1,-1,u""])
                #Create new row (bottom)
                AddRow(self.i)
                self.i+=1
                for x in range(evt.GetId()+1,self.i):
                    updateRow(x)
                self.mySizer.Layout()
                if self.i==2:
                    self.mySizer.FindItemAtPosition((0,4)).GetWindow().Enable(True)
                if self.i==maxRules:
                    for x in range(0,maxRules):
                        self.mySizer.FindItemAtPosition((x,3)).GetWindow().Enable(False)
            panel.EnableButtons(False) # New row is empty => allways not valid

        def OnRemoveButton(evt):
            """Event handler for the button '-' click."""
            CheckEnable=False #validityCheck "OFF"
            row=evt.GetId()
            if self.i>1:
                tp=SONG_TABLE_FIELDS[listRules2[self.i-1][0]][1]
                if tp<>"D":
                    rng=(0,)
                else:
                    if listRules2[self.i-1][1]>5:
                        rng=(1,0)
                    else:
                        rng=(2,1,0)
                #Remove last record
                del listRules2[row]
                #Remove last row
                infoSizer=self.mySizer.FindItemAtPosition((self.i-1,2)).GetSizer()
                for indx in rng:
                    wnd=infoSizer.GetChildren()[indx].GetWindow()
                    infoSizer.Detach(wnd)
                    wnd.Destroy()
                self.mySizer.Detach(infoSizer)
                infoSizer.Destroy()
                for col in (0,1,3,4):
                    myWnd=self.mySizer.FindItemAtPosition((self.i-1,col)).GetWindow()
                    self.mySizer.Detach(myWnd)
                    myWnd.Destroy()
                if self.i==maxRules:
                    for x in range(0,maxRules-1):
                        self.mySizer.FindItemAtPosition((x,3)).GetWindow().Enable(True)
                if self.i==2:
                    self.mySizer.FindItemAtPosition((0,4)).GetWindow().Enable(False)
                self.mySizer.Layout()
                self.i-=1
                for x in range(row,self.i):
                    updateRow(x)
            CheckEnable=True #validityCheck "ON"
            validityCheck()

        def OnRandom(evt=None):
            rnd = randomChkBoxCtrl.GetValue()
            if rnd:
                order = False
                orderChkBoxCtrl.SetValue(False)
                shuffle = 0
                shuffleChkBoxCtrl.SetValue(0)
            OnOrderSwitch()
            orderChkBoxCtrl.Enable(not rnd)
            shuffleChkBoxCtrl.Enable(not rnd)
            if evt is not None:
                validityCheck()

        def OnOrderSwitch(evt=None):
            enbl=orderChkBoxCtrl.GetValue()
            if enbl and critCtrl.GetSelection()==-1:
                critCtrl.SetSelection(wx.FindWindowById(50).GetSelection())
            dirCtrl.Enable(enbl)
            dirTxt1.Enable(enbl)
            dirTxt2.Enable(enbl)
            critCtrl.Enable(enbl)
            if evt is not None:
                validityCheck()

        def OnLimitSwitch(evt=None):
            enbl=limitChkBoxCtrl.GetValue()
            numCtrl.Enable(enbl)
            limitTxt1.Enable(enbl)
            limitTxt2.Enable(enbl)
            if evt is not None:
                validityCheck()

        def OnEventInterception(evt):
            validityCheck()
        radioBoxMode.Bind(wx.EVT_RADIOBOX, OnEventInterception)
        nameCtrl.Bind(wx.EVT_TEXT, OnEventInterception)
        repeatChkBoxCtrl.Bind(wx.EVT_CHECKBOX, OnEventInterception)
        shuffleChkBoxCtrl.Bind(wx.EVT_CHECKBOX, OnEventInterception)
        crossfadeChkBoxCtrl.Bind(wx.EVT_CHECKBOX, OnEventInterception)
        clearPlaylistChkBoxCtrl.Bind(wx.EVT_CHECKBOX, OnEventInterception)

        randomChkBoxCtrl.Bind(wx.EVT_CHECKBOX, OnRandom)
        orderChkBoxCtrl.Bind(wx.EVT_CHECKBOX, OnOrderSwitch)
        limitChkBoxCtrl.Bind(wx.EVT_CHECKBOX, OnLimitSwitch)
        OnRandom()
        OnLimitSwitch()
#----------------------
        self.i=len(listRules2)
        CheckEnable=False #validityCheck "OFF"
        for x in range(0,len(listRules2)):
            AddRow(x)
            updateRow(x)
        if self.i==1:
            self.mySizer.FindItemAtPosition((0,4)).GetWindow().Enable(False)
        if self.i==maxRules:
            for x in range(0,maxRules):
                self.mySizer.FindItemAtPosition((x,3)).GetWindow().Enable(False)
        if listRules2[0][0]==-1: #For new created empty filter
            panel.EnableButtons(False)
        CheckEnable=True #validityCheck "ON"
        self.myDirty=False

        while panel.Affirmed():
            panel.SetResult(
            nameCtrl.GetValue(),
            radioBoxMode.GetSelection(),
            listRules2,
            orderChkBoxCtrl.GetValue(),
            dirCtrl.GetSelection(),
            choices.index(critCtrl.GetStringSelection()),
            limitChkBoxCtrl.GetValue(),
            randomChkBoxCtrl.GetValue(),
            numCtrl.GetValue(),
            repeatChkBoxCtrl.Get3StateValue(),
            shuffleChkBoxCtrl.Get3StateValue(),
            crossfadeChkBoxCtrl.Get3StateValue(),
            clearPlaylistChkBoxCtrl.GetValue(),
            )

    class text:
        radioboxMode = "Select songs ..."
        modeAnd = "Matches all rules (AND)"
        modeOr = "Matches at least one rule (OR)"
        equal = "is equal to"
        notEqual = "is not equal to"
        greater = "is greater than"
        greatOrEqual = "is greater than or equal to"
        less = "is less than"
        lowerOrEqual = "is less than or equal to"
        notStartsWith = "does not start with"
        startsWith = "starts with"
        endsWith = "ends with"
        notEndsWith = "does not end with"
        includes = "includes"
        notIncludes = "excludes"
        isEmpty = "is empty"
        isNotEmpty = "is non-empty"
        beforeLess = "was earlier than - before ..."   #RELATIVE TO 'NOW' MODE
        beforeMore = "was later than - before ..."     #RELATIVE TO 'NOW' MODE
        filterName = "Filter name:"
        found = "%s/%s songs found"
        noFound = "no song found"
        asc = "ascending"
        desc = "descending"
        order1 = "Songs found are sorted in"
        order2 = "order by:"
        limit1 = "Select only the first"
        limit2 = "entry"
        #seconds = "seconds"
        minutes = "minutes"
        hours = "hours"
        days = "days"
        months = "months"
        years = "years"
#====================================================================

class LoadPlaylistBySql(eg.ActionBase):
    name = "Load Playlist by SQL query"
    description = ur'''<rst>**Loads a MediaMonkey playlist defined by SQL query.**

This action is for advanced users only. Here you can write your own SQL
query and use it to select specific songs from **MediaMonkey**'s
database . You need only enter the *WHERE* clause into the text field,
but you can also add an *ORDER BY* clause and *LIMIT* clause
(I tried the OFFSET clause too, but without success).

The simplest query might look like this: **Artist = "Beatles"**.

**MediaMonkey**'s database is based on **SQLite**.
Its documentation can be found at http://www.sqlite.org/docs.html .
If you are new to SQL, you might get some help from a SQL tutorial.
A good example can be found at http://www.firstsql.com/tutor2.htm .
Pay particular attention to the `WHERE Clause`_ .

.. _`WHERE Clause`: http://www.firstsql.com/tutor2.htm#where'''

    def __init__(self):
        self.myDirty=None
        text=self.text

    def __call__(
        self,
        plName,
        sql,
        repeat,
        shuffle,
        crossfade,
        clear,
    ):
        if not self.plugin.workerThread2:
            self.plugin.workerThread2 = MediaMonkeyWorkerThread(self.plugin)
            self.plugin.workerThread2.Start(100.0)
        args = (
            self.plugin.workerThread2.LoadSqlPlaylist,
            plName,
            sql,
            repeat,
            shuffle,
            crossfade,
            clear,
        )
        self.plugin.workerThread2.Call(partial(*args))

    def Configure(
        self,
        plName="",
        query="",
        repeat=2,
        shuffle=2,
        crossfade=2,
        clear = True,
    ):
        tmpDict=dict(zip(Text.SongTableFields,[tpl[0] for tpl in SONG_TABLE_FIELDS]))
        panel = eg.ConfigPanel(self,resizable = True)
        text = self.text
        mySizer = wx.GridBagSizer(10, 10)
        sqlTextCtrl=wx.TextCtrl(panel,-1,query,style=wx.TE_MULTILINE)
        mySizer.Add(sqlTextCtrl, (0,0), (1, 3), flag = wx.EXPAND)
        mySizer.Add(
            wx.StaticText(panel, -1, text.cols),(1,0),(1,2),
            flag = wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT
        )
        choices=['']
        choices.extend(Text.SongTableFields)
        choices.sort()
        colCtrl=wx.Choice(panel,-1 , choices=choices)
        mySizer.Add(colCtrl, (2,0), (1, 1), flag = wx.ALIGN_LEFT|wx.TOP,border = -6)
        callSizer = wx.BoxSizer(wx.VERTICAL)
        nameSizer = wx.BoxSizer(wx.VERTICAL)
        statBox = wx.StaticBox(panel, -1, "")
        stBsizer = wx.StaticBoxSizer(statBox, wx.HORIZONTAL)
        #stBsizer.SetMinSize((240,-1))
        stBsizer.Add(nameSizer,1,wx.EXPAND)
        nameCtrl = wx.TextCtrl(panel, -1, plName)
        nameSizer.Add(wx.StaticText(panel, -1, text.filterName),0,wx.LEFT)
        nameSizer.Add(nameCtrl, 0,wx.TOP|wx.EXPAND,4)
        mySizer.Add(stBsizer, (1,2), (2, 1), flag = wx.ALIGN_RIGHT)

        repeatChkBoxCtrl = wx.CheckBox(panel, label=Text.repeat,style=wx.CHK_3STATE|wx.CHK_ALLOW_3RD_STATE_FOR_USER)
        repeatChkBoxCtrl.Set3StateValue(repeat)
        shuffleChkBoxCtrl = wx.CheckBox(panel, label=Text.shuffle,style=wx.CHK_3STATE|wx.CHK_ALLOW_3RD_STATE_FOR_USER)
        shuffleChkBoxCtrl.Set3StateValue(shuffle)
        crossfadeChkBoxCtrl = wx.CheckBox(panel, label=Text.crossfade,style=wx.CHK_3STATE|wx.CHK_ALLOW_3RD_STATE_FOR_USER)
        crossfadeChkBoxCtrl.Set3StateValue(crossfade)
        clearPlaylistChkBoxCtrl = wx.CheckBox(panel, label=Text.clearPlaylist)
        clearPlaylistChkBoxCtrl.SetValue(clear)
        mySizer.Add(repeatChkBoxCtrl,(3,0),(1,1),flag=wx.ALIGN_LEFT)
        mySizer.Add(shuffleChkBoxCtrl,(3,1),(1,1),flag=wx.ALIGN_LEFT)
        mySizer.Add(crossfadeChkBoxCtrl,(4,0),(1,1),flag=wx.ALIGN_LEFT)
        mySizer.Add(clearPlaylistChkBoxCtrl,(4,1),(1,2),flag=wx.RIGHT)
        headLabel = wx.StaticText(panel, -1, text.head)
        panel.sizer.Add(headLabel,flag = wx.ALIGN_CENTER_VERTICAL)
        panel.sizer.Add(mySizer, 1, flag = wx.EXPAND|wx.TOP,border=4)
        headLabel.SetFocus()

        mySizer.AddGrowableRow(0)
        mySizer.AddGrowableCol(0)
        mySizer.AddGrowableCol(1)
        mySizer.AddGrowableCol(2)

        def onColCtrl(evt):
            field = colCtrl.GetStringSelection()
            if field:
                col = tmpDict[field]
                sqlTextCtrl.WriteText(col)
        colCtrl.Bind(wx.EVT_CHOICE,onColCtrl)

        def onSqlTextCtrl(evt):
            colCtrl.SetSelection(0)
            evt.Skip()
        sqlTextCtrl.Bind(wx.EVT_TEXT,onSqlTextCtrl)

        while panel.Affirmed():
            panel.SetResult(
            nameCtrl.GetValue(),
            sqlTextCtrl.GetValue(),
            repeatChkBoxCtrl.Get3StateValue(),
            shuffleChkBoxCtrl.Get3StateValue(),
            crossfadeChkBoxCtrl.Get3StateValue(),
            clearPlaylistChkBoxCtrl.GetValue(),
            )

    class text:
        filterName = "Filter name:"
        found = "%s/%s songs found"
        noFound = "no song found"
        asc = "ascending"
        desc = "descending"
        order1 = "Songs found are sorted in"
        order2 = "order by:"
        limit1 = "Select only the first"
        limit2 = "entry"
        head = "Condition part of SQL statement (WHERE clause):"
        cols = "Insert typical field from MM Songs table:"
#====================================================================

class Jukebox(eg.ActionBase):
    name = "Album jukebox"
    description = u'''<rst>**Album jukebox**.

Thanks to this action, your PC can function like a jukebox. In order to work
properly, the event which triggers this action, must be carrying a payload.
If this payload is a valid ID of an album, MediaMonkey will immediately start
playing this album.

One way to trigger an event with a payload is to use the plugin **Multitap**
(Numpad mode).'''

    def Configure(self,repeat=2,shuffle=2,crossfade=2,clear = True, filePath='', stop = True):
        txt = self.text
        self.filePath = filePath
        panel = eg.ConfigPanel(self)
        label1Text = wx.StaticText(panel, -1, self.text.label1)
        if filePath:
            startFolder = path_split(filePath)[1]
        else:
            startFolder = eg.folderPath.Documents
        filePathCtrl = eg.FileBrowseButton(
            panel,
            size=(410,-1),
            toolTip = self.plugin.text.toolTipFolder,
            dialogTitle = self.plugin.text.browseTitle,
            buttonText = eg.text.General.browse,
            initialValue=startFolder+'\\AlbumListMM.txt',
            fileMask="CSV files (*.csv)|*.csv|"\
                "Text file (*.txt)|*.txt|"\
                "All files (*.*)|*.*",
        )
        if filePath:
            filePathCtrl.SetValue(filePath)

        Sizer = panel.sizer
        sizes = []
        sizes.append(panel.GetTextExtent(txt.saveButton)[0])
        sizes.append(panel.GetTextExtent(txt.openButton)[0])
        w=max(sizes)+24
        exportButton = wx.Button(panel, -1, txt.saveButton, size = ((w,-1)))
        exportButton.SetToolTip(wx.ToolTip(txt.baloonBttn[0]))
        openButton = wx.Button(panel, -1, txt.openButton, size = ((w,-1)))
        openButton.SetToolTip(wx.ToolTip(txt.baloonBttn[1] % self.filePath))
        repeatChkBoxCtrl = wx.CheckBox(panel, label=Text.repeat,style=wx.CHK_3STATE|wx.CHK_ALLOW_3RD_STATE_FOR_USER)
        shuffleChkBoxCtrl = wx.CheckBox(panel, label=Text.shuffle,style=wx.CHK_3STATE|wx.CHK_ALLOW_3RD_STATE_FOR_USER)
        crossfadeChkBoxCtrl = wx.CheckBox(panel, label=Text.crossfade,style=wx.CHK_3STATE|wx.CHK_ALLOW_3RD_STATE_FOR_USER)
        clearPlaylistChkBoxCtrl = wx.CheckBox(panel, label=Text.clearPlaylist)
        stopChkBoxCtrl = wx.CheckBox(panel, label=Text.stopPlaying)
        clearPlaylistChkBoxCtrl.SetValue(clear)
        stopChkBoxCtrl.SetValue(stop)
        repeatChkBoxCtrl.Set3StateValue(repeat)
        shuffleChkBoxCtrl.Set3StateValue(shuffle)
        crossfadeChkBoxCtrl.Set3StateValue(crossfade)
        Sizer.Add(repeatChkBoxCtrl,0)
        Sizer.Add(shuffleChkBoxCtrl,0,wx.TOP,10)
        Sizer.Add(crossfadeChkBoxCtrl,0,wx.TOP,10)
        Sizer.Add(clearPlaylistChkBoxCtrl,0,wx.TOP,10)
        Sizer.Add(stopChkBoxCtrl,0,wx.TOP,10)
        Sizer.Add(label1Text, 0, wx.TOP,15)
        Sizer.Add(filePathCtrl,0,wx.TOP,3)
        Sizer.Add(exportButton,0,wx.TOP,10)
        Sizer.Add(openButton,0,wx.TOP,10)

        def onBtnClick(event):
            self.filePath=filePathCtrl.GetValue()
            self.jukeboxFrame = JukeboxFrame(parent=self)
            wx.CallAfter(
                self.jukeboxFrame.ShowJukeboxFrame,
                'Album',
                clearPlaylistChkBoxCtrl.GetValue(),
                filePathCtrl.GetValue(),
                True,
                stopChkBoxCtrl.GetValue(),
                repeatChkBoxCtrl.Get3StateValue(),
                shuffleChkBoxCtrl.Get3StateValue(),
                crossfadeChkBoxCtrl.Get3StateValue(),
            )
            openButton.Enable(True)
            event.Skip()
        exportButton.Bind(wx.EVT_BUTTON, onBtnClick)

        def onOpenBtnClick(event):
            if isfile(self.filePath):
                flag = False
            else:
                flag = True
                self.filePath=filePathCtrl.GetValue()
            self.jukeboxFrame = JukeboxFrame(parent = self)
            wx.CallAfter(
                self.jukeboxFrame.ShowJukeboxFrame,
                'Album',
                clearPlaylistChkBoxCtrl.GetValue(),
                filePathCtrl.GetValue(),
                flag,
                stopChkBoxCtrl.GetValue(),
                repeatChkBoxCtrl.Get3StateValue(),
                shuffleChkBoxCtrl.Get3StateValue(),
                crossfadeChkBoxCtrl.Get3StateValue(),
            )
            event.Skip()
        openButton.Bind(wx.EVT_BUTTON, onOpenBtnClick)

        while panel.Affirmed():
            panel.SetResult(
                repeatChkBoxCtrl.Get3StateValue(),
                shuffleChkBoxCtrl.Get3StateValue(),
                crossfadeChkBoxCtrl.Get3StateValue(),
                clearPlaylistChkBoxCtrl.GetValue(),
                filePathCtrl.GetValue(),
                stopChkBoxCtrl.GetValue()
            )

    def GetLabel(self,repeat,shuffle,crossfade,clear,filePath,stop):
        return self.name

    def __call__(self,repeat=2,shuffle=2,crossfade=2,clear=True,filePath='',stop=True):
        ID = eg.event.payload
        self.plugin.Jubox(
            ID,
            clear,
            repeat,
            shuffle,
            crossfade,
            stop,
        )

    class text():
        label1 = "Target file to export albums:"
        saveButton = "Export album list to file"
        openButton = "Open album list file"
        baloonBttn =('''ATTENTION !!! This operation may take several minutes !!!
Click to export code, album name and album artist to selected file.
You can this file import for example to MS Excel or OOo Calc''',
            'Open file %s'
        )
#====================================================================

class SongJukebox(eg.ActionBase):
    name = "Song jukebox"
    description = u'''<rst>**Song jukebox**.

Thanks to this action, your PC can function like a jukebox. In order to work
properly, the event which triggers this action, must be carrying a payload.
If this payload is a valid ID of a song, MediaMonkey will immediately start
playing the song.

One way to trigger an event with a payload is to use the plugin **Multitap**
(Numpad mode).'''

    def Configure(self,clear=False,filePath='',stop = True):
        self.filePath = filePath
        txt = self.text
        panel = eg.ConfigPanel(self)
        label1Text = wx.StaticText(panel, -1, self.text.label1)
        if filePath:
            startFolder = path_split(filePath)[1]
        else:
            startFolder = eg.folderPath.Documents
        filePathCtrl = eg.FileBrowseButton(
            panel,
            size=(410,-1),
            toolTip = self.plugin.text.toolTipFolder,
            dialogTitle = self.plugin.text.browseTitle,
            buttonText = eg.text.General.browse,
            initialValue=startFolder+'\\SongListMM.txt',
            fileMask="CSV files (*.csv)|*.csv|"\
                "Text file (*.txt)|*.txt|"\
                "All files (*.*)|*.*",
        )
        if filePath:
            filePathCtrl.SetValue(filePath)
        Sizer = panel.sizer
        sizes = []
        sizes.append(panel.GetTextExtent(txt.saveButton)[0])
        sizes.append(panel.GetTextExtent(txt.openButton)[0])
        w=max(sizes)+24
        exportButton = wx.Button(panel, -1, txt.saveButton, size=((w,-1)))
        exportButton.SetToolTip(wx.ToolTip(txt.baloonBttn[0]))
        openButton = wx.Button(panel, -1, txt.openButton, size=((w,-1)))
        openButton.SetToolTip(wx.ToolTip(txt.baloonBttn[1] % self.filePath))
        clearPlaylistChkBoxCtrl = wx.CheckBox(panel, label=Text.clearPlaylist)
        clearPlaylistChkBoxCtrl.SetValue(clear)
        stopChkBoxCtrl = wx.CheckBox(panel, label=Text.stopPlaying)
        stopChkBoxCtrl.SetValue(stop)
        Sizer.Add(clearPlaylistChkBoxCtrl,0,wx.TOP,10)
        Sizer.Add(stopChkBoxCtrl,0,wx.TOP,10)
        Sizer.Add(label1Text, 0, wx.TOP,15)
        Sizer.Add(filePathCtrl,0,wx.TOP,3)
        Sizer.Add(exportButton,0,wx.TOP,20)
        Sizer.Add(openButton,0,wx.TOP,20)

        def onBtnClick(event):
            self.filePath=filePathCtrl.GetValue()
            self.jukeboxFrame = JukeboxFrame(parent = self)
            wx.CallAfter(
                self.jukeboxFrame.ShowJukeboxFrame,
                'Song',
                clearPlaylistChkBoxCtrl.GetValue(),
                filePathCtrl.GetValue(),
                True,
                stopChkBoxCtrl.GetValue(),
            )
            openButton.Enable(True)
            event.Skip()
        exportButton.Bind(wx.EVT_BUTTON, onBtnClick)

        def onOpenBtnClick(event):
            if isfile(self.filePath):
                flag = False
            else:
                self.filePath=filePathCtrl.GetValue()
                flag = True
            self.jukeboxFrame = JukeboxFrame(parent = self)
            wx.CallAfter(
                self.jukeboxFrame.ShowJukeboxFrame,
                'Song',
                clearPlaylistChkBoxCtrl.GetValue(),
                filePathCtrl.GetValue(),
                flag,
                stopChkBoxCtrl.GetValue(),
            )
            event.Skip()
        openButton.Bind(wx.EVT_BUTTON, onOpenBtnClick)

        while panel.Affirmed():
            panel.SetResult(
                clearPlaylistChkBoxCtrl.GetValue(),
                filePathCtrl.GetValue(),
                stopChkBoxCtrl.GetValue()
            )

    def GetLabel(self,clear,filePath, stop):
        return self.name

    def __call__(self,clear=True,filePath='', stop = True):
        ID = eg.event.payload
        self.plugin.SongJubox(
            ID,
            clear,
            stop
        )

    class text():
        label1 = "Target file to export songs:"
        saveButton = "Export song list to file"
        openButton = "Open song list file"
        baloonBttn = ('''ATTENTION !!! This operation may take several minutes !!!
Click to export code, song name and song artist to selected file.
You can this file import for example to MS Excel or OOo Calc''',
            'Open file %s'
        )
#====================================================================

class SendKeys(eg.ActionBase):
    name = "Send keys"
    description = '''<rst>**Sends keys to MediaMonkey Window**.

Some features of MediaMonkey (for example executing a script) can be controlled
from another program only using the hotkeys . On the menu
*Tools - Options - General - Hotkeys*, set an appropriate hotkeys
(you don't need to set the hotkeys as global !) and then in the text box *"Keystroke to send"*
type the same (for example *Shift+Alt+E*). In the text box *"Name of hotkey"*
you can label the keystrokes with your own description.'''

    def __call__(self, descr = "",keys = ""):
        hwnds = MM_WindowMatcher()
        if hwnds:
            eg.SendKeys(hwnds[0],u'{'+keys+u'}', False)

    def Configure(self,descr="",keys = ""):
        text  = self.Text
        panel = eg.ConfigPanel(self)
        Sizer = panel.sizer
        #descrLabel.Enable(False)
        nameLabel = wx.StaticText(panel,-1,text.nameLabel)
        nameCtrl = wx.TextCtrl(panel,-1,descr)
        keysLabel = wx.StaticText(panel,-1,text.keysLabel)
        keysCtrl = wx.TextCtrl(panel,-1,keys)
        Sizer.Add(nameLabel,0,wx.TOP,18)
        Sizer.Add(nameCtrl,0,wx.TOP,4)
        Sizer.Add(keysLabel,0,wx.TOP,18)
        Sizer.Add(keysCtrl,0,wx.TOP,4)
        while panel.Affirmed():
            panel.SetResult(
            nameCtrl.GetValue(),
            keysCtrl.GetValue(),
        )
    def GetLabel(self,descr="",keys = ""):
        if descr != '':
            return descr
        else:
            return keys

    class Text():
        nameLabel = "Name of hotkey:"
        keysLabel = "Keystroke to send (e.g. Shift+Alt+E):"

#===============================================================================

class JukeboxFrame(wx.Frame):

    def __init__(self, parent):
        self.plugin = parent.plugin
        wx.Frame.__init__(
            self,
            None,
            -1,
            size=(-1, -1),
            style=wx.CAPTION|wx.RESIZE_BORDER
        )
        self.SetBackgroundColour(wx.NullColour)
        self.menuFlag = False
        self.parent=parent

    def ShowJukeboxFrame(
        self,
        case,
        clear,
        filePath,
        create,
        stop,
        repeat = None,
        shuffle = None,
        crossfade = None,
    ):

        eg.Bind("MediaMonkey.jukebox",self.CompleteForm)
        self.case = case
        self.clear = clear
        self.filePath = filePath
        self.create = create
        self.repeat = repeat
        self.shuffle = shuffle
        self.crossfade = crossfade
        self.stop = stop
        self.text = self.plugin.text
        mainSizer = wx.BoxSizer(wx.VERTICAL)
        centralSizer = wx.GridBagSizer(10, 10)
        centralSizer.AddGrowableRow(0)
        centralSizer.AddGrowableCol(1)
        if case == 'Album':
            cols = self.text.labelsA
        else:
            cols = self.text.labelsS
        self.itemListCtrl = wx.ListCtrl(self, -1, style=wx.LC_REPORT | wx.VSCROLL | wx.HSCROLL)
        for i, label in enumerate(cols):
            self.itemListCtrl.InsertColumn(
                i,
                label,
            )
        centralSizer.Add(self.itemListCtrl, (0,0),(1,3), flag = wx.EXPAND)

        #Buttons
        self.playButton = wx.Button(self, -1, self.text.popup)
        self.playButton.Enable(False)
        centralSizer.Add(self.playButton,(1,0), flag = wx.ALIGN_LEFT)
        self.closeButton = wx.Button(self, -1, self.text.close)
        centralSizer.Add(self.closeButton,(1,2), flag = wx.ALIGN_RIGHT)

        self.itemListCtrl.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnRightClick)
        self.itemListCtrl.Bind(wx.EVT_LIST_ITEM_SELECTED, self.ListSelection)
        self.itemListCtrl.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.ListSelection)

        def OnSize(event):
            w = self.GetSize()[0]/2-52
            self.itemListCtrl.SetColumnWidth(1, w)
            self.itemListCtrl.SetColumnWidth(2, w)
            event.Skip()

        mainSizer.Add(centralSizer, 1,wx.EXPAND|wx.ALL,10)
        mainSizer.Layout()
        self.SetSizer(mainSizer)

        def OnCloseWindow(event):
            eg.Unbind("MediaMonkey.jukebox",self.CompleteForm)
            self.Show(False)
            self.MakeModal(False)
            self.Destroy()
            event.Skip()

        self.Bind(wx.EVT_CLOSE, OnCloseWindow)
        self.playButton.Bind(wx.EVT_BUTTON, self.onPlayButton)
        self.closeButton.Bind(wx.EVT_BUTTON, self.onCloseButton)
        self.SetMinSize((475,200))
        self.CentreOnParent()
        self.SetTitle(self.filePath)
        sizes = (55,200,200)
        for i in range(3):
            self.itemListCtrl.SetColumnWidth(i, sizes[i])
        self.SetDimensions(-1, -1, 504, 400, sizeFlags=wx.SIZE_AUTO)
        self.Bind(wx.EVT_SIZE, OnSize)
        self.MakeModal(True)
        self.Show(True)
        self.RefreshList()

    def RefreshList(self):
        self.patientFrame = None
        if not self.create:
            self.CompleteForm()
        else:
            self.itemListCtrl.DeleteAllItems()
            self.Update()
            if self.plugin.workerThread:
                self.patientFrame = PatientFrame(self,self.text.please)
                if self.case=='Album':
                    self.plugin.workerThread.Call(partial(self.plugin.workerThread.ExportAlbumList,self.filePath))
                else:
                    self.plugin.workerThread.Call(partial(self.plugin.workerThread.ExportSongList,self.filePath))
                self.Enable(False)

    def CompleteForm(self,evt=None):
        row = 0
        file = codecs.open(self.filePath,encoding='utf-8', mode='r',errors='replace')
        albums = file.readlines()
        for album in albums:
            item = album.split('\t')
            self.itemListCtrl.InsertStringItem(row,item[0])
            self.itemListCtrl.SetStringItem(row, 1,item[1])
            self.itemListCtrl.SetStringItem(row, 2, item[2][:-1])
            row += 1
        file.close()
        if self.patientFrame:
            self.patientFrame.Destroy()
        self.Enable(True)
        self.Raise()

    def onCloseButton(self, evt):
        self.Close(True)
        #evt.Skip()

    def ListSelection(self, event=None):
        self.menuFlag = self.itemListCtrl.GetSelectedItemCount() == 1
        self.playButton.Enable(self.menuFlag)
        if event:
            event.Skip()

    def onPlayButton(self, evt):
        item = self.itemListCtrl.GetFirstSelected()
        ID = self.itemListCtrl.GetItemText(item)
        if self.case == 'Album':
            self.plugin.Jubox(
                ID,
                self.clear,
                self.repeat,
                self.shuffle,
                self.crossfade,
                self.stop
            )
        else:
            self.plugin.SongJubox(ID,self.clear,self.stop)
        evt.Skip()

    def OnRightClick(self, event):
        if not hasattr(self, "popupID1"):
            self.popupID1 = wx.NewId()

            self.Bind(wx.EVT_MENU, self.onPlayButton, id=self.popupID1)

        # make a menu
        menu = wx.Menu()
        # add some items
        if self.menuFlag:
            menu.Append(self.popupID1, self.text.popup)

        # Popup the menu.  If an item is selected then its handler
        # will be called before PopupMenu returns.
        self.PopupMenu(menu)
        menu.Destroy()
#===============================================================================

class UnaccessibleTracksFrame(wx.Frame):

    def __init__(self, parent):
        self.parent=parent
        self.plugin = parent.plugin
        wx.Frame.__init__(
            self,
            None,
            -1,
            size=(-1, -1),
            style=wx.CAPTION|wx.RESIZE_BORDER
        )
        self.SetBackgroundColour(wx.NullColour)
        self.menuFlag = False

    def ShowUnaccessibleTracksFrame(
        self,
        filePath,
        mode=True
    ):
        eg.Bind("MediaMonkey.unaccessible",self.CompleteForm)
        self.filePath = filePath
        self.mode = mode
        self.text = self.plugin.text
        mainSizer = wx.BoxSizer(wx.VERTICAL)
        centralSizer = wx.GridBagSizer(10, 10)
        centralSizer.AddGrowableRow(0)
        centralSizer.AddGrowableCol(1)
        centralSizer.AddGrowableCol(3)
        cols = self.text.labelsU
        self.itemListCtrl = wx.ListCtrl(self, -1, style=wx.LC_REPORT | wx.VSCROLL | wx.HSCROLL)
        for i, label in enumerate(cols):
            self.itemListCtrl.InsertColumn(
                i,
                label,
            )
        centralSizer.Add(self.itemListCtrl, (0,0),(1,5), flag = wx.EXPAND)

        #Buttons
        self.deleteButton = wx.Button(self, -1, self.text.popup2)
        self.deleteButton.Enable(False)
        centralSizer.Add(self.deleteButton,(1,0), flag = wx.ALIGN_LEFT)
        self.refreshButton = wx.Button(self, -1, self.text.refresh)
        centralSizer.Add(self.refreshButton,(1,2), flag = wx.ALIGN_CENTER)
        self.closeButton = wx.Button(self, -1, self.text.close)
        centralSizer.Add(self.closeButton,(1,4), flag = wx.ALIGN_RIGHT)

        self.itemListCtrl.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnRightClick)
        self.itemListCtrl.Bind(wx.EVT_LIST_ITEM_SELECTED, self.ListSelection)
        self.itemListCtrl.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.ListSelection)

        def OnSize(event):
            w = self.GetSize()[0]/4-26
            self.itemListCtrl.SetColumnWidth(1, w)
            self.itemListCtrl.SetColumnWidth(2, 3*w)
            event.Skip()

        mainSizer.Add(centralSizer, 1,wx.EXPAND|wx.ALL,10)
        mainSizer.Layout()
        self.SetSizer(mainSizer)

        def OnCloseWindow(event):
            eg.Unbind("MediaMonkey.unaccessible",self.CompleteForm)
            self.Show(False)
            self.MakeModal(False)
            self.Destroy()
            self.parent.frameIsOpen = False

        self.Bind(wx.EVT_CLOSE, OnCloseWindow)
        self.deleteButton.Bind(wx.EVT_BUTTON, self.onDeleteButton)
        self.refreshButton.Bind(wx.EVT_BUTTON, self.onRefreshButton)
        self.closeButton.Bind(wx.EVT_BUTTON, self.onCloseButton)
        self.SetMinSize((475,200))
        self.CentreOnParent()
        self.SetTitle(filePath)
        row = 0
        sizes = (55,100,300)
        for i in range(2):
            self.itemListCtrl.SetColumnWidth(i, sizes[i])
        self.SetDimensions(-1, -1, 504, 400, sizeFlags=wx.SIZE_AUTO)
        self.Bind(wx.EVT_SIZE, OnSize)
        self.MakeModal(True)
        self.Show(True)
        self.onRefreshButton()

    def onCloseButton(self, evt):
        self.Close(True)

    def ListSelection(self, event=None):
        self.menuFlag = self.itemListCtrl.GetSelectedItemCount() > 0
        self.deleteButton.Enable(self.menuFlag)
        if event:
            event.Skip()

    def onDeleteButton(self, evt):
        item = self.itemListCtrl.GetFirstSelected()
        tmpList=[]
        while item != -1:
            ID = self.itemListCtrl.GetItemText(item)
            tmpList.append(ID)
            item = self.itemListCtrl.GetNextSelected(item)
        for n in range(self.itemListCtrl.GetItemCount()-1,-1,-1):
            for item in tmpList:
                if item == self.itemListCtrl.GetItemText(n):
                    if self.plugin.DeleteSong(item) == "0":
                        self.itemListCtrl.DeleteItem(n)
                    break
        n = self.itemListCtrl.GetItemCount()
        if n > 0:
            file = codecs.open(self.filePath,encoding='utf-8', mode='w',errors='replace')
            for item in range(n):
                file.write('\t'.join((
                    self.itemListCtrl.GetItemText(item),
                    self.itemListCtrl.GetItem(item,1).GetText(),
                    self.itemListCtrl.GetItem(item,2).GetText(),
                )))
                file.write('\n')
            file.close()
        elif isfile(self.filePath):
            remove_file(self.filePath)
        evt.Skip()

    def onRefreshButton(self,evt=None):
        self.patientFrame = None
        if not evt and not self.mode and isfile(self.filePath):
            self.CompleteForm()
        else:
            self.itemListCtrl.DeleteAllItems()
            self.Update()
            if self.plugin.workerThread:
                self.patientFrame = PatientFrame(self,self.text.please)
                self.plugin.workerThread.Call(partial(self.plugin.workerThread.GetNotAccessibleTracks,self.filePath))
                self.Enable(False)
        if evt:
            evt.Skip()

    def CompleteForm(self,evt=None):
#        self.MakeModal(True)
        if isfile(self.filePath):
            file = codecs.open(self.filePath,encoding='utf-8', mode='r',errors='replace')
            tracks = file.readlines()
            row = 0
            for track in tracks:
                item = track.split('\t')
                self.itemListCtrl.InsertStringItem(row,item[0])
                self.itemListCtrl.SetStringItem(row, 1,item[1])
                self.itemListCtrl.SetStringItem(row, 2,item[2][:-1])
                row += 1
            file.close()
        if self.patientFrame:
            self.patientFrame.Destroy()
        self.Enable(True)
        self.Raise()

    def OnRightClick(self, event):
        if not hasattr(self, "popupID1"):
            self.popupID1 = wx.NewId()

            self.Bind(wx.EVT_MENU, self.onDeleteButton, id=self.popupID1)

        # make a menu
        menu = wx.Menu()
        # add some items
        if self.menuFlag:
            menu.Append(self.popupID1, self.text.popup2)

        # Popup the menu.  If an item is selected then its handler
        # will be called before PopupMenu returns.
        self.PopupMenu(menu)
        menu.Destroy()
#===============================================================================

class PatientFrame(wx.Frame):
    def __init__(
        self,
        parent,
        message,
    ):
        if parent is None and eg.document.frame:
            parent = eg.document.frame
        wx.Frame.__init__(self, parent, -1, eg.APP_NAME, wx.DefaultPosition, style=wx.RAISED_BORDER| wx.STAY_ON_TOP)
        self.SetBackgroundColour(wx.NullColour)

        sizer = wx.BoxSizer(wx.HORIZONTAL)
        bmp = wx.ArtProvider.GetBitmap(wx.ART_WARNING, wx.ART_CMN_DIALOG, (32, 32))
        staticBitmap = wx.StaticBitmap(self, -1, bmp)
        staticBitmap2 = wx.StaticBitmap(self, -1, bmp)
        staticText = wx.StaticText(self, -1, message)
        fnt = staticText.GetFont()
        fnt.SetPointSize(13)
        staticText.SetFont(fnt)
        sizer.Add(staticBitmap, 0, wx.ALL, 12)
        sizer.Add(staticText,0,wx.ALIGN_CENTER)
        sizer.Add(staticBitmap2, 0, wx.ALL, 12)
        self.SetSizerAndFit(sizer)
        self.CenterOnParent()
        self.Show()
        self.Update()
#===============================================================================

ACTIONS = (
    (GetIsRunning, 'GetIsRunning', 'Get IsRunning', 'Get IsRunning Status.', None),
    (GetSomeInfo, 'GetVolume', 'Get Volume', 'Get Volume.', (100,'Volume')),
    (GetSomeInfo, 'GetBalance', 'Get Balance', 'Get Balance.', (100,'Panning')),
    (GetSomeInfo, 'GetRepeat', 'Get Repeat', 'Get Repeat Status.', (1,'isRepeat')),
    (GetShuffle, 'GetShuffle', 'Get Shuffle', 'Get Shuffle Status.', None),
    (GetSomeInfo, 'GetAutoDJ', 'Get AutoDJ', 'Get AutoDJ Status.', (1,'isAutoDJ')),
    (GetSomeInfo, 'GetCrossfade', 'Get Crossfade', 'Get Crossfade Status.', (1,'isCrossfade')),
    (GetSomeInfo, 'GetPosition', 'Get Position in ms', 'Get Position in ms.', (1,'PlaybackTime')),
)

ACTIONS2 = (
    (GetSongInfo, 'GetBasicSongInfo', 'Get Basic Song Info', 'Get Basic Song Info.', 0),
    (GetSongInfo, 'GetBasicSongInfoNextTrack', 'Get Basic Song Info Of Next Track', 'Get Basic Song Info Of Next Track.', 1),
    (GetSongInfo, 'GetBasicSongInfoPreviousTrack', 'Get Basic Song Info Of Previous Track', 'Get Basic Song Info Of Previous Track.', -1),
)