EventGhost/EventGhost

View on GitHub
plugins/RadioSure/__init__.py

Summary

Maintainability
F
5 mos
Test Coverage
# -*- coding: utf-8 -*-

version="0.2.22"

# plugins/RadioSure/__init__.py
#
# Copyright (C)  2009 - 2013   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.2.22 by Pako 2013-01-26 12:17 UTC+1
#     - bugfix - scheduler size, when system font > 100%
# 0.2.21 by Pako 2012-12-02 13:06 UTC+1
#     - added "Stop processing event" feature (Menu frame)
# 0.2.20 by Pako 2011-08-24 09:12 UTC+1
#     - bugfix - wrong stored last position of manager and scheduler
# 0.2.19 by Pako 2011-06-27 13:54 UTC+1
#     - added action "Get last played favorite station" (retrieved from RS menu)
#     - action name "Start (Stop) observation of titlebar" changed to
#                   "Start (Stop) periodical observation"
#     - added option to observation of last played station (from file RadioSure.xml)
#     - bugfix - extended timeout for temporarily launched RS during plugin start
# 0.2.18 by Pako 2011-06-10 18:44 UTC+1
#     - bugfix - write to logfile when Schedule title is non-ascii
# 0.2.17 by Pako 2011-06-10 16:27 UTC+1
#     - Added action "Get favorites"
#     - Added period "minutes" for schedule type "Periodically"
# 0.2.16 by Pako 2011-06-08 18:21 UTC+1
#     - eg.scheduler used instead of the Threading
# 0.2.15 by Pako 2011-06-05 18:31 UTC+1
#     - Used eg.EVT_VALUE_CHANGED instead of EVT_BUTTON_AFTER
# 0.2.14 by Pako 2011-05-08 18:28 UTC+1
#     - Bug fix - action Minimize/Restore
# 0.2.13 by Pako 2011-04-06 11:07 UTC+1
#     - Bug fix - bad name of language file
# 0.2.12 by Pako 2011-04-05 17:38 UTC+1
#     - Added first version of Favorites manager
#     - Added "menu browser"
#     - Added many new actions
# 0.2.11 by Pako 2011-03-03 09:08 UTC+1
#     - The cursor is changed to indicate the existence of a context menu
#     - If exists file "contextCursor.cur", used as the cursor where there is a contextual menu
# 0.2.10 by Pako 2011-02-12 09:53 UTC+1
#     - FixedTimeCtrl replaced by eg.TimeCtrl
# 0.2.9 by Pako 2011-01-15 11:50 UTC+1
#     - different shape of the cursor on the table of schedules indicate that there is context menu available
# 0.2.8 by Pako 2011-01-11 14:25 UTC+1
#     - if you turn on logging then into the log file is written whole command line
# 0.2.7 by Pako 2011-01-07 18:39 UTC+1
#     - fixed bug - the Scheduler window opens although in Scheduler.xml there not the attribute "Position"
#       (this can happen when you upgrade from version 0.2.0 and lower)
# 0.2.6 by Pako 2011-01-07 11:39 UTC+1
#     - fixed bug - incorrect reading favorites, when applied a new structure of RadioSure.xml file
# 0.2.5 by Pako 2010-12-28 16:02 UTC+1
#     - added popup menu and features "Move schedule up/down"
# 0.2.4 by Pako 2010-12-24 12:08 UTC+1
#     - there is no need to reinstall this plugin, when changing the way the installation (especially the paths) of Radio?Sure!
# 0.2.3 by Pako 2010-12-24 08:30 UTC+1
#     - scheduler dialog opens, even though there is no node "Favorites" in RadioSure.xml
# 0.2.2 by Pako 2010-12-19 15:54 UTC+1
#     - changed the way of paths settings to the RadioSure.exe and RadioSure.xml
# 0.2.1 by Pako 2010-12-19 08:19 UTC+1
#     - scheduler dialog remembers its position even after closing EventGhost
#     - bugfix - "Add schedule" enable buttons, when schedule list is empty
# 0.2.0 by Pako 2010-12-14 11:13 UTC+1
#     - a comprehensive rework according to the plugin SchedulGhost:
#     - addded new types of schedule
#     - changed format of "Scheduler.xml" file
#     - added ability to affect certain types of schedules according to public holidays
#     - added option to select the first day of the week (Sunday or Monday)
#     - scheduler dialog remembers its position
#     - scheduler dialog is not modal and can be minimized
#     - added Apply button (scheduler dialog)
#     - added new actions - "Run schedule immediately"
# 0.1.9 by Pako 2010-12-09 13:52 UTC+1
#     - correction of previous versions (moreover redefine one pseudo-private method)
# 0.1.8 by Pako 2010-12-06 20:10 UTC+1
#     - wx.lib.masked.TimeCtrl workaround (see http://trac.wxwidgets.org/ticket/11171)
# 0.1.7 by Pako 2010-07-22 20:27 GMT+1
#     - bugfix
# 0.1.6 by Pako 2010-07-22 10:30 GMT+1
#     - added wx.ComboBox for Scheduler actions
# 0.1.5 by Pako 2010-07-10 08:21 GMT+1
#     - added Scheduler
#     - added guid attribute
# 0.1.4 by Pako 2010-03-23 11:20 GMT+1
#     - added action Random favorite
# 0.1.3 by Pako 2010-03-22 09:09 GMT+1
#     - added actions Start and Stop observation of titlebar
#===============================================================================

eg.RegisterPlugin(
    name = "RadioSure",
    author = "Pako",
    version = version,
    kind = "program",
    guid = "{84703620-87B4-4982-A9AB-DA1B3F8D22EA}",
    description = ur'''<rst>
Adds actions to control the `Radio?Sure!`_

.. _Radio?Sure!: http://www.radiosure.com/ ''',
    createMacrosOnAdd = True,
    url = "http://www.eventghost.net/forum/viewtopic.php?f=9&t=2359",
    icon = (
        "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAADAFBMVEUA//+Gh4ju7/Ds"
        "7e7q6+zo6evm5+nk5efi4+Xg4ePd3+Db3N7Z2tzW2Nrr7e5+g4bo6erm5+js7e3a3N7Y"
        "2dt3fYDT1dfp6uzn6Orl5uhIS03V1tjS1NbQ0tTn6Onl5ufi5OXS09XP0dPNz9HR09XP"
        "0NPMztDKzM7h4+Tf4OLd3uCVmp1OUlQZGhoYGRlLTlCHjZDMzdDJy87Hycve4OHc3t+d"
        "oqQyNDU3OjtSVlgpKywqLC2IjpHHyMvExsnc3d/Z29xWWlyBh4p2fH9pbnFfZGYsLi9L"
        "T1HExsjCxMYbHB2XnJ5MUFJKTU9yeHtVWVvBw8a/wcTW19kcHR6UmZypra9RVVeGjI9l"
        "am0aGxu/wcO9v8JcYWNeY2W5vL6xtLamqqyboKK9vsG7vcA9QEG6vL+5u76LkJPIycyy"
        "tbddYmRYXV+jqKqDiYy3ubzFx8nDxcfBw8W4ur22uLsAAAAAAAAAAAAAAAAAAAAAAAAA"
        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADQcfgAAAAAAAXQciD0AAAA"
        "AAAAAAAAAAAAAAAAAAAAAAAAAAABAAgAAAAAAAAAAAAAAAAAAAAAAAAAAABGa1gAAAAA"
        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
        "AAAAAAAAAAEAAAAAAAAAAAAPAAAAAAEAAAEAAADQckT/C08AAAAAAAAAAAAAAAMAAADf"
        "BnAAAAAAAAAAAAAAAAAAAAQAAQEAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACnZAh6AAAA"
        "AXRSTlMAQObYZgAAAAlwSFlzAAALEgAACxIB0t1+/AAAAKdJREFUeNpjYGBgRAVoAl9A"
        "ArwQCbDMayYgg5OTk5GBg4PhPzs7OwNIAESDARsbGwNICxtQKQeQLwSkb4EE1JEMPQfS"
        "wgMGjNwgANZix8h4UwOqYgdIxdmznGKcIPAMaBVIReARW6DcNW2QimUgAWlGe7DynTY8"
        "jPOYwNYzs/+//vqB3ANmZrAWVUeg9EsGCcafHIyTQQKGjDZAAUYOJt4/rH0M6N4HAFCJ"
        "GrcTFgV2AAAAAElFTkSuQmCC"
    ),
)
#===============================================================================

import wx.grid as gridlib
import subprocess
import wx.calendar as wxCal
from wx.lib.masked import EVT_TIMEUPDATE
from subprocess import Popen
from os import listdir, remove, rename
from os.path import abspath, join, dirname, split, isfile, exists
from calendar import day_name, month_name, monthrange
from wx.lib.mixins.listctrl import CheckListCtrlMixin
from _winreg import OpenKey, HKEY_CURRENT_USER, EnumValue, QueryValueEx, CloseKey
from time import sleep, mktime, strptime, localtime
from datetime import datetime as dt
from datetime import timedelta as td
from copy import deepcopy as cpy
from xml.dom import minidom as miniDom
from threading import Timer
from eg.WinApi.Utils import GetMonitorDimensions
from eg.WinApi.Dynamic import CreateEvent, SetEvent, PostMessage
from eg.WinApi.Dynamic import SendMessage, ShowWindow, RegisterWindowMessage
from eg.WinApi import SendMessageTimeout
from win32gui import GetWindowText, GetWindow, GetDlgCtrlID, GetMenuItemCount
from win32gui import GetWindowPlacement, GetDlgItem, GetClassName, GetSubMenu
from win32file import GetFileAttributes
from random import randrange
from codecs import lookup
from codecs import open as openFile
from winsound import PlaySound, SND_ASYNC
from locale import strxfrm
from ctypes import c_long, c_ulong, c_int, byref, sizeof, Structure, c_buffer
from ctypes.wintypes import WinDLL
_kernel32 = WinDLL("kernel32")
_user32 = WinDLL("user32")
from sys import getfilesystemencoding
FSE = getfilesystemencoding()

if eg.Version.base >= "0.4.0":
    from eg.Classes.MainFrame.TreeCtrl import DropTarget as EventDropTarget
    IMAGES_DIR = eg.imagesDir
else:
    from eg.Classes.MainFrame.TreeCtrl import EventDropTarget
    IMAGES_DIR = eg.IMAGES_DIR

ARIAL_INFO  = "0;-35;0;0;0;700;0;0;0;0;3;2;1;34;Arial"
TAHOMA_INFO = "0;-27;0;0;0;400;0;0;0;0;3;2;1;34;Tahoma"

PROCESS_TERMINATE     = 1
WM_CLOSE              = 16
WM_COMMAND            = 273
WM_SYSCOMMAND         = 274
TBM_GETPOS            = 1024
TBM_SETPOS            = 1029
SC_RESTORE            = 61728
#SW_HIDE               = 0
#SW_MINIMIZE           = 6
SW_RESTORE            = 9
GW_CHILD              = 5
GW_HWNDNEXT           = 2
FILE_ATTRIBUTE_HIDDEN = 2
FILE_ATTRIBUTE_SYSTEM = 4
SYS_VSCROLL_X = wx.SystemSettings.GetMetric(wx.SYS_VSCROLL_X)
#===============================================================================

CUR_STRING = (
    "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAABnRSTlMA/wBmAADomHeP"
    "AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAMK0lEQVR42gEgDN/zAQAAAOns8xZ6DQAAAAAA"
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAApKDTXa9EAAAAAAAAAAAAA"
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAA////AQEB1tLSKZQuAAAAAAAAAAAA"
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAP///youLtds0gAAAAAAAAAAAAAAAAAA"
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
    "AAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAD///8qLi7XbNIAAAAAAAAAAAAAAAAAAAAA"
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
    "AAAAAAAAAAAAAAACAAAAAAAA/wD+AAAA////Ki4u12zSAAAAAAAAAAAAAAAAAAAAAAAA"
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
    "AAAAAAAAAgAAAAAAAP7///8A/gAAAP///youLtds0wAAAAAAAAAAAAAAAAAAAAAAAAAA"
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
    "AAIAAAAAAAAAAAD+////AP4AAAD//PwqLi3YbNQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAA"
    "AAAAAAAAAAAA/gD/APwAAAMD9PjwKS4s2W3UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAA"
    "AAABAAH6AQAAAPb69gkHC+ns9CgtLdlt1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAQAC+wLz"
    "+/P0+/QB+QHw8fEWEwvs6ecoLS/VcN8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAA/////vv+9/32AwMDAvsC"
    "AQkABQEHAAAAAAAAAAAAAQEJ/2b4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAIFAgsIDObm8gYKBwUBBwEB"
    "AQEBDQEBAQEBCAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAADz9vsjIBicn6UAAAAAABTd2Nj/"
    "ZgD/Zvn/ZgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
    "AAAAAAAAAAAAAAAAAAAAAAABAAAA////8/X6DgwH/2YAAZoA////9fHwDBAR/2YAAAAA"
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
    "AAAAAAAAAAAAAAAAAgAAAAAAAA4MB+Pk6wAAAAAAFMnFwgsPEAAEGLtQxAAAAAAAAAAA"
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
    "AAAAAAAAAAQAAAABAQHe3+YcghUAAADk59ocGRL////o4coYGx7/ZgAAAAAAAAAEoBoA"
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD8YOYAAAAAAAAAAAAAAAAAAAAAAAAA"
    "AAAE/2YAAAAAAIcaAAAAAAAAG38SAAAQ8u7qFxodAAAAAAAAAAAAAAAAAAAAiodz+Pj4"
    "DAwMAQEBBQUF/Pz8+Pj47e3tCAgIg4aaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAA"
    "AAAAAAAAAAAAAAAAAAAAAPz/7wQBASEkOszP2ACXKAAAAAAAAAAAAFBQUCMjIwAAAAAA"
    "AAAAAAAAAAAAAHJycoeHh4OGmgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAA"
    "AAAAAAAAAAAAAAADZwH/ZgAAAMYAlygAAAAAAAAAAAAAAAAiIiLMzMwFBQULCwsAAAAB"
    "AQHn5+cXFxcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAA"
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeHh46OjoAAAABQUF6enp+fn5CAgI"
    "BgYGISEhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAA"
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM7OzqysrA8PD4WFhYKCgvz8/AgICIKCgouL"
    "iwMGGgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH/ZgAAAAAAAAAAAAAAAAAAAAAAAAAA"
    "AAAAAAAAAAAAAAAAAAAAAAAEoBr9+uY2NjbKysogICDg4OAAAAAAAAAAAAAAAAADBhr8"
    "YOYAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
    "AAAAAAAAAAAAAAAAAAAAwcHBlZWVAAAA39/fZWVlAAAAysrKnZ2ds7OzAAAAAAAAAAAA"
    "AAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
    "AAAAAAAAAAAAAD4+PqysrAAAAAAAAJqamgAAAKmpqRMTE0xMTAAAAAAAAAAAAAAAAAAA"
    "AAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
    "AAAAAAAAAAAtLS0AAADb29sEBAQAAAAxMTHt7e0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
    "AAAAAAAAAAAB/2YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABKAa"
    "/PnlAAAADQ0NCQkJs7OzZmZm0dHRAAAAAAAABAcb/GDmAAAAAAAAAAAAAAAAAAAAAAAA"
    "AAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMrKygEB"
    "AfT09Ovr65aWlmRkZAEBAURERAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIA"
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2Njb/////////"
    "///u7u79/f3n5+e8vLwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/2YAAAAA"
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABKAaAAAAAAAAAAAAAAAAAAAA"
    "AAAAAAAAAAAAAAAAAAAA/GDmAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf9mAAAAAAAAAAAA"
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAA"
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADPbTswenz69gAAAABJRU5ErkJggg=="
)
#===============================================================================
class ConfigData(eg.PersistentData):
    pos = None
    plcmnt = None

class Text:
    label1 = "Radio?Sure! installation folder:"
    label2 = "RadioSure.xml and Scheduler.xml folder location:"
#    filemask = "RadioSure.exe|RadioSure.exe|All-Files (*.*)|*.*"
    text1 = "Couldn't find RadioSure window !"
    browseTitle = "Selected folder:"
    toolTipFolder = "Press button and browse to select folder ..."
    boxTitle = 'Folder "%s" is incorrect'
    toolTipFile = "Press button and browse to select logfile ..."
    browseFile = 'Select the logfile'
    boxMessage1 = 'Missing file %s !'
    logLabel = "Log scheduler events to following logfile:"
#    nextRun = "Next run: %s"
    none = "None"
    execut = 'Schedule "%s" - execution. Next run: %s'
    cmdLine = 'Commandline: %s'
    cancAndDel = 'Schedule "%s" canceled and deleted'
    cancAndDis = 'Schedule "%s" canceled (disabled)'
    newSched = 'Schedule "%s" scheduled. Next run: %s'
    re_Sched = 'Schedule "%s" re-scheduled. New next run: %s'
    start = 'RadioSure plugin started. All valid schedules will be scheduled:'
    stop = 'RadioSure plugin stoped. All scheduled schedules will be canceled:'
    canc = 'Schedule "%s" canceled'
    launched = "Schedule.Launched"
    holidButton = "Public holidays ..."
    managerButton = "Show scheduler"
    fixBoxLabel = "Fixed public holidays:"
    varBoxLabel = "Variable public holidays:"
    ok = "OK"
    cancel = "Cancel"
    yes = "Yes"
    no = "No"
    add = "Add"
    delete = "Delete"
    first_day = "The first day of the week:"
    xmlComment = "Radio?Sure! scheduler configuration file. Updated at %s."
    messBoxTit0 = "EventGhost - Radio?Sure! plugin"
    messBoxTit1 = "Attention !"
    message2 = """You can not start another instance of Radio?Sure!,
because the maximum number of instances %i is exhausted!"""
    message3 = '''You can not start another instance of Radio?Sure!,
because the option "Allow only one instance" is chosen!'''
    autoClose = "Auto close after %i s"
    toolTip = "Drag-and-drop an event from the log into the box."
    popup = (
        "Delete item",
        "Delete all items",
    )
    clear  = "Clear all"
    opened = "Opened"
    closed = "Closed"
    root = "Main (root) menu"

    class OpenManager:
        dialogTitle = "Radio?Sure! Favorites manager %s  (plugin for EventGhost)"
        toolTipDelete = "Delete item(s)"
        toolTipUp = "Move item(s) up"
        toolTipDown = "Move item(s) down"
        moveTop = "Move item(s) top"
        moveBottom = "Move item(s) bottom"
        exportSel = "Export selected item(s) to XML file"
        exportAll = "Export all items to XML file"
        toolTipExport = "Export selected (if any) or all items to XML file"
        toolTipImport = "Import from XML file"
        toolTipImportSR = "Import from Screamer Radio"
        sort  = "Sort alphabetically"
        play = "Play selected favorite just now !"
        refresh = "Refresh favorites list from RadioSure.xml"
        export = "Export"
        imprt = "Import"
        importSR = "Import SR"
        lblSource = "Source:"
        lblGenre = "Genre:"
        lblLanguage = "Language:"
        lblCountry = "Country:"
        ok = "OK"
        cancel = "Cancel"
        apply = "Apply"
        lblList = "Favorites list:"
        xmlComment1 = "Radio?Sure! favorites backup file."
        xmlComment2 = 'Saved at %s by EventGhost.'
        choose = 'Choose a XML file to be import'
        save = 'Backup favorites as XML file ...'
        wildcard = "XML file (*.xml)|*.xml"
        removeDupl = "Remove duplications"
        messBoxTit2 = """Attention !
Radio?Sure! is running !"""
        messBoxTit3 = """Attention !
Recording is in progress !"""
        messBoxTit5 = "Congratulations!"
        messBoxTit6 = "Announcement"
        messBoxTit7 = "Warning"
        message1 = """Your version of Radio?Sure! allows you to save only the first %i favorite stations !
Other favorites will be ignored."""
        message2 = """If you want to save the modified list of favorite stations,
must be overwritten file RadioSure.xml.
You can not overwrite the file RadioSure.xml,
if the Radio?Sure! is currently running.
Otherwise, the favorites list is returned to its original condition.

Press button %s, if the program Radio?Sure! can be closed.
Press button %s, if the program Radio?Sure! can not be closed."""
        message3 = "Failed to save data to the file RadioSure.xml !"
        message4 = 'It is not possible to import because there is a problem.\n\
The file "%s" does not have the expected structure.'
        message5 = "Your list of favorite stations has been successfully updated!"
        message6 = "Failed to close Radio?Sure!"
        message7 = "Your list of favorite stations has not been updated!"
        message8 = """Your list of favorite stations contain (in sources) duplications!
They will be saved only unique items."""
        message9 = "Failed to open Radio?Sure!"

    class OpenScheduler:
        dialogTitle = "Radio?Sure! Scheduler %s  (plugin for EventGhost)"
        header = (
            "Enabled",
            "Schedule title",
            "Last run",
            "Next run",
        )
        sched_type = (
            "Only once (or yearly)",
            "Daily",
            "Weekly",
            "Monthly / weekday",
            "Monthly / day",
            "Periodically",
        )
        toolTipFile = """Press button and browse to select file ...
File type (as .mp3) need not be completed. Will be added automatically."""

        browseTitle = "Select a folder and enter file name (without file type):"
        serial_num = (
            "first",
            "second",
            "third",
            "fourth",
            "fifth",
            "last"
        )
        the = "The"
        in_ = "in"
        buttons = (
            "Add new",
            "Duplicate",
            "Delete",
            "OK",
            "Cancel",
            "Apply"
        )
        type_label = "Schedule type:"
        source = "Source URL:"
        favorite = "Favorite station title:"
        filename = "Destination file name (optional):"
        chooseDay = "Choose day"
        theEvery = "The every"
        yearly = "Every year on the same day"
        chooseTime = "Choose start time and duration (00:00 = constantly)"
        choosePeriod = "Choose period"
        andThenEvery = "Repeat every"
        units = (
            "minutes",
            "hours",
            "days",
            "weeks",
            "months",
            "years",
        )
        start = "Start time (HH:MM:SS):"
        length = "Duration (HH:MM):"
        boxTitle = "Your setup is not properly configured !"
        boxTexts = (
            "Schedule title must not be an empty string !",
            "Schedule title must be unique !",
            'Determine the source URL, or set the mode "Do nothing" !',
            'Not allowed to set "Do nothing" while also "None" event !',
            'Must be chosen Schedule type !',
            "The span must be shorter than the period !",
        )
        workModeLabel = "Radio?Sure! working mode:"
        workModes = (
            "Playing (audibly)",
            "Recording (audibly)",
            "Recording (soundlessly)",
            "Do nothing"
        )
        windOpenLabel = "Window open:"
        windOpenChoices =(
            "Visible",
            "Hidden"
        )
        triggEvtLabel = "Trigger an event:"
        triggEvtChoices = (
            "None",
            "Schedule title",
            "All parameters"
        )
        testButton = "Test now"
        testRun = 'Schedule "%s" - TEST execution. Possible next run: %s'
        holidCheck_1 = "Do not trigger events for a chosen day if it happens to be a holiday"
        holidCheck_2 = "Do also trigger events for a non-chosen day if it happens to be a holiday"
        popup = (
            "Add schedule",
            "Duplicate schedule",
            "Delete schedule",
            "Enable all schedules",
            "Disable all schedules",
            "Move schedule up",
            "Move schedule down",
        )
#===============================================================================

def my_list2cmdline(seq):
    """ FIXING subprocess.list2cmdline
    Workaround, because subprocess.list2cmdline does not work with arguments like:
    filename="... ...". Ie, when we need quotes inside the string, and somewhere
    inside is a space character. When you properly prepare all items
    (including the quotes), it works!
    There is also done simultaneously filesystemencode encoding
    (otherwise there UnicodeDecodeError occurs...)"""
    return ' '.join([arg.encode(FSE) if isinstance(arg, unicode) else arg for arg in seq])
subprocess.list2cmdline = my_list2cmdline
#===============================================================================

class MyDirBrowseButton(eg.DirBrowseButton):
    def GetTextCtrl(self):          #  now I can make build-in textCtrl
        return self.textControl     #  non-editable !!!
#===============================================================================

class MyFileBrowseButton(eg.FileBrowseButton):
    def GetTextCtrl(self):          #  now I can make build-in textCtrl
        return self.textControl     #  non-editable !!!
#===============================================================================

class MySpinIntCtrl(eg.SpinIntCtrl):
    def SetNumCtrlId(self, id):
        self.numCtrl.SetId(id)
#===============================================================================

class MessageBoxDialog(wx.Dialog):

    def __init__(
        self,
        parent,
        message,
        caption = eg.APP_NAME,
        flags=wx.OK,
        time=0,
        plugin=None,
        pos=wx.DefaultPosition
    ):
        PlaySound('SystemExclamation', SND_ASYNC)
        if parent is None and eg.document.frame:
            parent = eg.document.frame
        dialogStyle = wx.DEFAULT_DIALOG_STYLE
        if flags & wx.STAY_ON_TOP:
            dialogStyle |= wx.STAY_ON_TOP
        wx.Dialog.__init__(self, parent, -1, caption, pos, style=dialogStyle)
        self.SetTitle(plugin.text.messBoxTit0)
        self.SetIcon(plugin.info.icon.GetWxIcon())
        bttns = []
        if flags:
            art = None
            if flags & wx.ICON_EXCLAMATION:
                art = wx.ART_WARNING
            elif flags & wx.ICON_ERROR:
                art = wx.ART_ERROR
            elif flags & wx.ICON_QUESTION:
                art = wx.ART_QUESTION
            elif flags & wx.ICON_INFORMATION:
                art = wx.ART_INFORMATION
            if art is not None:
                bmp = wx.ArtProvider.GetBitmap(art, wx.ART_MESSAGE_BOX, (32,32))
                icon = wx.StaticBitmap(self, -1, bmp)
                icon2 = wx.StaticBitmap(self, -1, bmp)
            else:
                icon = (32,32)
                icon2 = (32,32)
            flag = True
            if flags & wx.YES:
                default = False
                if not flags & wx.NO_DEFAULT:
                    default = True
                    flag = False
                bttns.append((wx.ID_YES, plugin.text.yes, default))
            if flags & wx.NO:
                default = False
                if flags & wx.NO_DEFAULT:
                    default = True
                    flag = False
                bttns.append((wx.ID_NO, plugin.text.no, default))
            if flags & wx.OK:
                bttns.append((wx.ID_OK, plugin.text.ok, flag))
            if flags & wx.CANCEL:
                bttns.append((wx.ID_CANCEL, plugin.text.cancel, False))
            if not flags & (wx.OK | wx.CANCEL | wx.YES | wx.NO):
                bttns.append((wx.ID_OK, plugin.text.ok, True))
        else:
            bttns.append((wx.ID_OK, plugin.text.ok, True))
        if caption:
            caption = wx.StaticText(self, -1, caption)
            caption.SetFont(wx.Font(12, wx.SWISS, wx.NORMAL, wx.BOLD))
        message = wx.StaticText(self, -1, message)
        line = wx.StaticLine(self, -1, size=(1,-1), style = wx.LI_HORIZONTAL)
        bottomSizer = wx.BoxSizer(wx.HORIZONTAL)
        bottomSizer.Add((10, 1))

        if time:
            self.cnt = time
            txt = plugin.text.autoClose % self.cnt
            info = wx.StaticText(self, -1, txt)
            info.Enable(False)
            bottomSizer.Add(info, 0, wx.TOP, 3)

            def UpdateInfoLabel(evt):
                self.cnt -= 1
                txt = plugin.text.autoClose % self.cnt
                info.SetLabel(txt)
                if not self.cnt:
                    self.Close()

            self.Bind(wx.EVT_TIMER, UpdateInfoLabel)
            self.timer = wx.Timer(self)
            self.timer.Start(1000)
        else:
            self.timer = None

        bottomSizer.Add((5,1),1,wx.EXPAND)
        for bttn in bttns:
            b = wx.Button(self, bttn[0], bttn[1])
            if bttn[2]:
                #b.SetDefault()
                defBtn = b # SetDefault() workaround
            bottomSizer.Add(b, 0, wx.LEFT, 5)
        bottomSizer.Add((10, 1))
        topSizer = wx.BoxSizer(wx.HORIZONTAL)
        topSizer.Add(icon,0,wx.LEFT|wx.RIGHT,10)
        topSizer.Add((1,1),1,wx.EXPAND)
        topSizer.Add(caption,0,wx.TOP,5)
        topSizer.Add((1,1),1,wx.EXPAND)
        topSizer.Add(icon2,0,wx.LEFT|wx.RIGHT,10)
        mainSizer = wx.BoxSizer(wx.VERTICAL)
        mainSizer.Add(topSizer, 0, wx.EXPAND|wx.TOP|wx.BOTTOM,10)
        mainSizer.Add(message, 0, wx.EXPAND|wx.LEFT|wx.RIGHT,10)
        mainSizer.Add(line, 0, wx.EXPAND|wx.ALL,5)
        mainSizer.Add(bottomSizer, 0, wx.EXPAND|wx.BOTTOM,5)
        # SetDefault() workaround:
        defBtn.SetFocus()

        def OnButton(evt):
            self.SetReturnCode(evt.GetId())
            self.Close()
            evt.Skip()
        wx.EVT_BUTTON(self, -1, OnButton)

        def onClose(evt):
            if self.GetReturnCode() not in (wx.ID_OK, wx.ID_CANCEL, wx.ID_YES, wx.ID_NO):
                self.SetReturnCode(wx.ID_CANCEL)
            if self.timer:
                self.timer.Stop()
                del self.timer
            self.MakeModal(False)
            self.GetParent().Raise()
            self.Destroy()
        self.Bind(wx.EVT_CLOSE, onClose)

        self.SetSizer(mainSizer)
        self.Fit()

def MessageBox(parent, message, caption='', flags=0, time = 0, plugin = None):
    mssgbx = MessageBoxDialog(parent, message, caption, flags, time, plugin)
    val = mssgbx.ShowModal()
    return val
#===============================================================================

class MyTimer():
    def __init__(self, t, plugin):
        self.timer = Timer(t, self.Run)
        self.plugin = plugin
        self.timer.start()


    def Run(self):
        try:
            self.plugin.menuDlg.Close()
            self.plugin.menuDlg = None
        except:
            pass


    def Cancel(self):
        self.timer.cancel()
#===============================================================================

class HolidaysFrame(wx.Dialog):
    fixWin = None
    varWin = None
    fixHolidays = []
    varHolidays = []

    def __init__(self, parent, plugin):
        self.plugin = plugin
        wx.Dialog.__init__(
            self,
            parent,
            -1,
            style = wx.DEFAULT_DIALOG_STYLE,
            name = self.plugin.text.holidButton
        )
        self.SetIcon(self.plugin.info.icon.GetWxIcon())
        self.panel = parent
        self.fixHolidays, self.varHolidays = cpy(self.panel.holidays)
        self.Bind(wxCal.EVT_CALENDAR_DAY, self.OnChangeDay)


    def ShowHolidaysFrame(self):
        text = self.plugin.text
        self.SetTitle(self.plugin.text.holidButton)
        self.fixWin = CalendarPopup(self, False, self.plugin.first_day)
        self.varWin = CalendarPopup(self, True, self.plugin.first_day)
        calW, calH = self.fixWin.GetWinSize()
        fixLbl = wx.StaticText(self, -1, text.fixBoxLabel)
        variableLbl = wx.StaticText(self, -1, text.varBoxLabel)
        widthList = [self.GetTextExtent("30. %s 2000" % month)[0] +
            SYS_VSCROLL_X for month in list(month_name)]
        widthList.append(fixLbl.GetSize()[0])
        widthList.append(variableLbl.GetSize()[0])
        w = max(widthList) + 5
        self.SetMinSize((w + calW + 30, 2 * calH + 128))
        self.fixListBox = HolidaysBox(
            self,
            -1,
            size = wx.Size(w, 130),
            style = wx.LB_SINGLE|wx.LB_NEEDED_SB
        )
        self.fix_add_Btn = wx.Button(self, -1, text.add)
        self.fix_del_Btn = wx.Button(self, -1, text.delete)
        self.fix_del_Btn.Enable(False)
        self.varListBox = HolidaysBox(
            self,
            -1,
            size = wx.Size(w, 130),
            style = wx.LB_SINGLE|wx.LB_NEEDED_SB
        )
        self.var_add_Btn = wx.Button(self, -1, text.add)
        self.var_del_Btn = wx.Button(self, -1, text.delete)
        self.var_del_Btn.Enable(False)
        line = wx.StaticLine(self, -1, style = wx.LI_HORIZONTAL)
        sizer = wx.BoxSizer(wx.VERTICAL)
        fixSizer = wx.GridBagSizer(2, 8)
        fixSizer.SetMinSize((w + 8 + calW, -1))
        varSizer = wx.GridBagSizer(2, 8)
        varSizer.SetMinSize((w + 8 + calW, -1))
        fixSizer.Add(fixLbl, (0, 0))
        fixSizer.Add(self.fixListBox, (1, 0), (3, 1))
        fixSizer.Add(self.fix_add_Btn, (1, 1))
        fixSizer.Add((-1, 15), (2, 1))
        fixSizer.Add(self.fix_del_Btn, (3, 1))
        varSizer.Add(variableLbl, (0, 0))
        varSizer.Add(self.varListBox, (1, 0), (3,1))
        varSizer.Add(self.var_add_Btn, (1, 1))
        varSizer.Add((-1, 15), (2, 1))
        varSizer.Add(self.var_del_Btn, (3, 1))
        sizer.Add(fixSizer, 0, wx.EXPAND|wx.ALL, 8)
        sizer.Add((-1, 12))
        sizer.Add(varSizer, 0, wx.EXPAND|wx.ALL, 8)
        sizer.Add((1, 16))
        btn1 = wx.Button(self, wx.ID_OK)
        btn1.SetLabel(text.ok)
        btn1.SetDefault()
        btn2 = wx.Button(self, wx.ID_CANCEL)
        btn2.SetLabel(text.cancel)
        btnsizer = wx.StdDialogButtonSizer()
        btnsizer.AddButton(btn1)
        btnsizer.AddButton(btn2)
        btnsizer.Realize()
        sizer.Add(line, 0, wx.EXPAND)
        sizer.Add((1,5))
        sizer.Add(btnsizer, 0, wx.EXPAND|wx.RIGHT, 10)
        sz = self.GetMinSize()
        self.SetSize(sz)
        self.fixListBox.Reset(self.fixHolidays)
        self.varListBox.Reset(self.varHolidays)
        self.Bind(wx.EVT_CLOSE, self.onClose)
        btn2.Bind(wx.EVT_BUTTON, self.onCancel)
        btn1.Bind(wx.EVT_BUTTON, self.onOK)
        self.fix_add_Btn.Bind(wx.EVT_BUTTON, self.onFixAddBtn)
        self.var_add_Btn.Bind(wx.EVT_BUTTON, self.onVarAddBtn)
        self.fix_del_Btn.Bind(wx.EVT_BUTTON, self.onFixDelBtn)
        self.var_del_Btn.Bind(wx.EVT_BUTTON, self.onVarDelBtn)
        self.Bind(wx.EVT_LISTBOX, self.onHolBoxSel)
        sizer.Layout()
        self.SetSizer(sizer)
        self.MakeModal(True)
        self.Show(True)


    def onClose(self, evt):
        self.MakeModal(False)
        self.GetParent().GetParent().Raise()
        self.Destroy()


    def onCancel(self, evt):
        self.Close()


    def onOK(self, evt):
        self.panel.holidays = (self.fixHolidays, self.varHolidays)
        self.Close()


    def onHolBoxSel(self, evt):
        if  evt.GetId() == self.fixListBox.GetId():
            self.fix_del_Btn.Enable(True)
        else:
            self.var_del_Btn.Enable(True)
        evt.Skip()


    def onFixAddBtn(self, evt):
        pos = self.ClientToScreen(self.fix_add_Btn.GetPosition())
        self.fixWin.PopUp(pos, self.fixHolidays)


    def onVarAddBtn(self, evt):
        pos = self.ClientToScreen(self.var_add_Btn.GetPosition())
        self.varWin.PopUp(pos, self.varHolidays)


    def onFixDelBtn(self, evt):
        self.fixHolidays.pop(self.fixListBox.GetSelection())
        if self.fixListBox.Reset(self.fixHolidays):
            self.fix_del_Btn.Enable(False)


    def onVarDelBtn(self, evt):
        self.varHolidays.pop(self.varListBox.GetSelection())
        if self.varListBox.Reset(self.varHolidays):
            self.var_del_Btn.Enable(False)


    def OnChangeDay(self, evt):
        if evt.GetId() == self.fixWin.GetCalId():
            self.fixListBox.Reset(self.fixHolidays)
        else:
            self.varListBox.Reset(self.varHolidays)
        evt.Skip()
#===============================================================================

class HolidaysBox(wx.ListBox):

    def __init__ (self, parent, id, size, style):
        wx.ListBox.__init__(
            self,
            parent = parent,
            id = id,
            size = size,
            style = style
        )
        self.sel = -1
        self.Bind(wx.EVT_LISTBOX, self.onHolBoxSel)


    def Reset(self, list):
        tmpList = []
        for item in list:
            day = item[-1]
            day = "  %i" % day if day < 10 else "%i" % day
            if len(item) == 2:
                tmpList.append("%s. %s" % (day, month_name[item[0]]))
            else:
                tmpList.append("%s. %s %i" % (day, month_name[item[1]], item[0]))
        self.Set(tmpList)
        if self.sel > -1 and self.sel < self.GetCount():
            self.SetSelection(self.sel)
            return False
        else:
            return True


    def  onHolBoxSel(self, evt):
        self.sel = evt.GetSelection()
        evt.Skip()
#===============================================================================

class CalendarPopup(wx.PopupWindow):
    yearChange = True

    def __init__(self, parent, yearChange, first_day):
        self.yearChange = yearChange
        wx.PopupWindow.__init__(self, parent)
        startDate = wx.DateTime()
        startDate.Set(1, 0)
        self.cal = wxCal.CalendarCtrl(
            self,
            -1,
            startDate,
            style = (wxCal.CAL_MONDAY_FIRST, wxCal.CAL_SUNDAY_FIRST)[first_day]
                | wxCal.CAL_SHOW_HOLIDAYS
                | wxCal.CAL_SEQUENTIAL_MONTH_SELECTION
                | wxCal.CAL_SHOW_SURROUNDING_WEEKS
        )
        self.cal.EnableYearChange(yearChange)
        sz = self.cal.GetBestSize()
        self.SetSize(sz)
        self.cal.Bind(wxCal.EVT_CALENDAR_DAY, self.OnChangeDay)
        self.cal.Bind(wxCal.EVT_CALENDAR_MONTH, self.OnChangeMonth)
        self.cal.Bind(wxCal.EVT_CALENDAR_YEAR, self.OnChangeMonth)
        self.cal.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow)


    def OnLeaveWindow(self, evt):
        self.PopDown()
        evt.Skip()


    def GetCalId(self):
        return self.cal.GetId()


    def GetWinSize(self):
        return self.GetSize()


    def OnChangeDay(self, evt):
        date = evt.GetDate()
        day, month, year = date.GetDay(), 1 + date.GetMonth(), date.GetYear()
        newHoliday = (year, month, day) if self.yearChange else (month, day)
        if not newHoliday in self.holidays:
            self.holidays.append(newHoliday)
            self.holidays.sort()
        date = self.cal.GetDate()
        self.cal.SetHoliday(day)
        date.AddDS(wx.DateSpan.Day())
        self.cal.SetDate(date)
        self.Refresh()
        evt.Skip()


    def OnChangeMonth(self, evt = None):
        date = self.cal.GetDate()
        cur_month = date.GetMonth() + 1   # convert wx.DateTime 0-11 => 1-12
        if self.yearChange:
            cur_year = date.GetYear()
            for year, month, day in self.holidays:
                if year == cur_year and month == cur_month:
                    self.cal.SetHoliday(day)
        else:
            for month, day in self.holidays:
                if month == cur_month:
                    self.cal.SetHoliday(day)


    def PopUp(self, position, holidays):
        self.cal.EnableHolidayDisplay(False)
        self.cal.EnableHolidayDisplay(True)
        self.SetPosition(position)
        self.holidays = holidays
        self.OnChangeMonth()
        self.Show(True)


    def PopDown(self):
        self.Show(False)
        self.Close()
#===============================================================================

class CheckListCtrl(wx.ListCtrl, CheckListCtrlMixin):

    def __init__(self, parent, text, width):
        wx.ListCtrl.__init__(
            self,
            parent,
            -1,
            size = (width, 164),
            style = wx.LC_REPORT|wx.LC_HRULES|wx.LC_VRULES|wx.LC_SINGLE_SEL
        )
        CheckListCtrlMixin.__init__(self)
        curFile = abspath(join(dirname(__file__), "contextCursor.cur"))
        img = None
        if exists(curFile):
            img = wx.EmptyImage(32, 32)
            img.LoadFile(curFile, wx.BITMAP_TYPE_CUR)
        if not img or not img.IsOk():
            from cStringIO import StringIO
            from base64 import b64decode
            stream = StringIO(b64decode(CUR_STRING))
            img = wx.ImageFromStream(stream)
            stream.close()
            img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_X, 0)
            img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_Y, 0)
        self.SetCursor(wx.CursorFromImage(img))
        self.selRow = -1
        self.back = self.GetBackgroundColour()
        self.fore = self.GetForegroundColour()
        self.selBack = wx.SystemSettings.GetColour(wx.SYS_COLOUR_HIGHLIGHT)
        self.selFore = wx.SystemSettings.GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT)
        for i, colLabel in enumerate(text.header):
            self.InsertColumn(i, colLabel)
        self.InsertStringItem(0, " ")
        self.SetColumnWidth(0, wx.LIST_AUTOSIZE_USEHEADER)
        self.SetStringItem(0, 1, "Test Name")
        self.SetStringItem(0, 2, "8888-88-88 88:88:88")
        self.SetColumnWidth(2, wx.LIST_AUTOSIZE_USEHEADER)
        col0 = self.GetColumnWidth(0)
        col23 = self.GetColumnWidth(2)
        self.SetColumnWidth(3, col23)
        self.SetColumnWidth(
            1,
            width - col0 - 2*col23 - SYS_VSCROLL_X-self.GetWindowBorderSize()[0]
        )
        self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected)


    def OnItemSelected(self, evt):
        self.SelRow(evt.m_itemIndex)
        evt.Skip()


    # this is called by the base class when an item is checked/unchecked !!!!!!!
    def OnCheckItem(self, index, flag):
        evt = eg.ValueChangedEvent(self.GetId(), value = (index, flag))
        wx.PostEvent(self, evt)


    def SelRow(self, row):
        if row != self.selRow:
            if self.selRow in range(self.GetItemCount()):
                item = self.GetItem(self.selRow)
                item.SetTextColour(self.fore)
                item.SetBackgroundColour(self.back)
                self.SetItem(item)
            self.selRow = row
        if self.GetItemBackgroundColour(row) != self.selBack:
            item = self.GetItem(row)
            item.SetTextColour(self.selFore)
            item.SetBackgroundColour(self.selBack)
            self.SetItem(item)
            self.SetItemState(row, 0, wx.LIST_STATE_SELECTED)


    def AppendRow(self):
        ix = self.GetItemCount()
        self.InsertStringItem(ix, "")
        self.CheckItem(ix)
        self.EnsureVisible(ix)
        self.SelRow(ix)
#===============================================================================

class ManagerDialog(wx.Dialog):

    def __init__(self, text, plugin):
        wx.Dialog.__init__(
            self,
            None,
            -1,
            text.dialogTitle % version,
            style = wx.DEFAULT_DIALOG_STYLE|wx.MINIMIZE_BOX|wx.CLOSE_BOX|wx.RESIZE_BORDER,
        )

        self.plugin = plugin
        statusRS = self.plugin.GetStatusRS()

        self.idUp      = wx.NewId()
        self.idDown    = wx.NewId()
        self.idTop     = wx.NewId()
        self.idBottom  = wx.NewId()
        self.idSort    = wx.NewId()
        self.idRefr    = wx.NewId()
        self.idPlay    = wx.NewId()
        self.SetIcon(self.plugin.info.icon.GetWxIcon())
        self.plugin.manager = self
        self.text = text
        self.Bind(wx.EVT_CLOSE, self.onClose)

        statPath = self.plugin.RadioSurePath+"\\Stations"
        rsd_files = [x for x in listdir(statPath) if x.endswith('.rsd') and x.startswith('stations-')]
        stations = statPath+"\\"+rsd_files[0]

        def unique(seq):
            res = set(seq)
            res = list(res)
            res.sort()
            return res

        f = openFile(stations, encoding='utf-8', mode='r')
        data = self.data = [item.split("\t") for item in f.readlines()]
        genres = [item[2] for item in data]
        genres = unique(genres)
        countrys = [item[3] for item in data]
        countrys = unique(countrys)
        languages = [item[4] for item in data]
        languages = unique(languages)
        titles = [item[0] for item in data]
        titles = unique(titles)
        f.close()

        curFile = abspath(join(dirname(__file__), "contextCursor.cur"))
        img = None
        if exists(curFile):
            img = wx.EmptyImage(32, 32)
            img.LoadFile(curFile, wx.BITMAP_TYPE_CUR)
        if not img or not img.IsOk():
            from cStringIO import StringIO
            from base64 import b64decode
            stream = StringIO(b64decode(CUR_STRING))
            img = wx.ImageFromStream(stream)
            stream.close()
            img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_X, 0)
            img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_Y, 0)
        self.grid = wx.ListCtrl(self, style = wx.LC_REPORT|wx.LC_NO_HEADER|wx.LC_HRULES|wx.LC_VRULES)
        self.grid.SetCursor(wx.CursorFromImage(img))
        self.grid.InsertColumn(0,"")

        #Button UP
        bmp = wx.ArtProvider.GetBitmap(wx.ART_GO_UP, wx.ART_OTHER, (16, 16))
        btnUP = wx.BitmapButton(self, self.idUp, bmp)
        btnUP.SetToolTipString(self.text.toolTipUp)
        #Button DOWN
        bmp = wx.ArtProvider.GetBitmap(wx.ART_GO_DOWN, wx.ART_OTHER, (16, 16))
        btnDOWN = wx.BitmapButton(self, self.idDown, bmp)
        btnDOWN.SetToolTipString(self.text.toolTipDown)
        #Button DEL
        bmp = wx.ArtProvider.GetBitmap(wx.ART_DELETE, wx.ART_OTHER, (16, 16))
        btnDEL = wx.BitmapButton(self, -1, bmp)
        btnDEL.SetToolTipString(self.text.toolTipDelete)

        btnExp = wx.Button(self, wx.ID_SAVEAS, self.text.export)
        btnExp.SetToolTipString(self.text.toolTipExport)

        btnImp = wx.Button(self, wx.ID_OPEN, self.text.imprt)
        btnImp.SetToolTipString(self.text.toolTipImport)

        btnImpSR = wx.Button(self, wx.ID_FILE, self.text.importSR)
        btnImpSR.SetToolTipString(self.text.toolTipImportSR)

        bmp = wx.ArtProvider.GetBitmap(wx.ART_HELP_SETTINGS, wx.ART_OTHER, (16, 16))
        btnSort = wx.BitmapButton(self, self.idSort, bmp)
        btnSort.SetToolTipString(self.text.sort)

        bmp = wx.ArtProvider.GetBitmap(wx.ART_REDO, wx.ART_OTHER, (16, 16))
        btnRefr = wx.BitmapButton(self, self.idRefr, bmp)
        btnRefr.SetToolTipString(self.text.refresh)



        def EnableCtrls():
            first = self.grid.GetFirstSelected()
            cnt = self.grid.GetSelectedItemCount()
            subseq = True
            for ix in range(first, first + cnt):
                if not self.grid.IsSelected(ix):
                    subseq = False
                    break
            one = cnt==1
            self.menuFlagM = subseq

            itemCnt = self.grid.GetItemCount()
            upDown = cnt > 0 and cnt < itemCnt and subseq
            sourceLabel.Enable(one)
            genreLabel.Enable(one)
            langLabel.Enable(one)
            countryLabel.Enable(one)
            sourceCtrl.Enable(one)
            btnUP.Enable(upDown)
            btnDOWN.Enable(upDown)
            btnDEL.Enable(cnt > 0)
            btnExp.Enable(itemCnt > 0)
            btnSort.Enable(itemCnt > 1)


        def ListSelection(event=None):
            EnableCtrls()
            first = self.grid.GetFirstSelected()
            cnt = self.grid.GetSelectedItemCount()
            if cnt == 1:
                item = self.tmpFavs[first]
                src = item[0]
                sourceCtrl.Clear()
                srcs = ()
                i = -1
                for ix in range(5, 11):
                    srcIx = [itm[ix] for itm in data]
                    if src in srcIx:
                        i = srcIx.index(src)
                        break
                if i > -1:
                    srcs = data[i][5:]
                    sourceCtrl.AppendItems(srcs)
                if not src in srcs:
                    sourceCtrl.Append(src)
                sourceCtrl.SetStringSelection(src)
                if item[2] in genres:
                    genreCtrl.SetStringSelection(item[2])
                if item[3] in languages:
                    langCtrl.SetStringSelection(item[3])
                if item[4] in countrys:
                    countryCtrl.SetStringSelection(item[4])
            else:
                sourceCtrl.SetSelection(-1)
                genreCtrl.SetSelection(-1)
                langCtrl.SetSelection(-1)
                countryCtrl.SetSelection(-1)
            if event:
                event.Skip()
        self.grid.Bind(wx.EVT_LIST_ITEM_SELECTED, ListSelection)
        self.grid.Bind(wx.EVT_LIST_ITEM_DESELECTED, ListSelection)


        def onRefresh(evt = None, seq = None):
            self.favs = seq if seq else self.plugin.RefreshVariables()
            self.tmpFavs = cpy(self.favs)
            self.grid.DeleteAllItems()
            for row in range(len(self.tmpFavs)):
                self.grid.InsertStringItem(row, self.tmpFavs[row][1])
            self.grid.SetColumnWidth(0, -1)
            self.grid.SetColumnWidth(0, self.grid.GetColumnWidth(0) + 6)
            ListSelection()
            self.Diff()
            EnableCtrls()
            #evt.Skip
        btnRefr.Bind(wx.EVT_BUTTON, onRefresh)


        def onSort(evt):
            self.tmpFavs = sorted(self.tmpFavs, key=lambda i: strxfrm(i[1].encode(eg.systemEncoding)))
            self.grid.DeleteAllItems()
            for row in range(len(self.tmpFavs)):
                self.grid.InsertStringItem(row, self.tmpFavs[row][1])
            ListSelection()
            self.Diff()
            self.Colour()
        btnSort.Bind(wx.EVT_BUTTON, onSort)

        sourceLabel = wx.StaticText(self, -1, self.text.lblSource)
        genreLabel = wx.StaticText(self, -1, self.text.lblGenre)
        langLabel = wx.StaticText(self, -1, self.text.lblLanguage)
        countryLabel = wx.StaticText(self, -1, self.text.lblCountry)
        sourceCtrl = wx.Choice(self, -1, choices=[])
        genreCtrl = wx.Choice(self, -1, choices=genres)
        langCtrl = wx.Choice(self, -1, choices=languages)
        countryCtrl = wx.Choice(self, -1, choices=countrys)
        genreCtrl.Enable(False)
        langCtrl.Enable(False)
        countryCtrl.Enable(False)
        line = wx.StaticLine(self, -1, style=wx.LI_HORIZONTAL)
        btn1 = wx.Button(self, wx.ID_OK, self.text.ok)
        btn1.SetDefault()
        btn2 = wx.Button(self, wx.ID_CANCEL, self.text.cancel)
        btn3 = wx.Button(self, wx.ID_APPLY, self.text.apply)
        btn1.Bind(wx.EVT_BUTTON, self.onBtn)
        btn2.Bind(wx.EVT_BUTTON, self.onBtn)
        btn3.Bind(wx.EVT_BUTTON, self.onBtn)
        btnExp.Bind(wx.EVT_BUTTON, self.onBtnsInOut)
        btnImp.Bind(wx.EVT_BUTTON, self.onBtnsInOut)
        btnImpSR.Bind(wx.EVT_BUTTON, self.onBtnsInOut)
        btnsizer = wx.BoxSizer(wx.HORIZONTAL)
        btnsizer.Add(btnExp,0,wx.LEFT)
        btnsizer.Add((8,-1),0)
        btnsizer.Add(btnImp,0,wx.CENTER)
        btnsizer.Add((8,-1),0)
        btnsizer.Add(btnImpSR,0,wx.CENTER)
        btnsizer.Add((-1,-1),1)
        btnsizer.Add(btn1,0,wx.CENTER)
        btnsizer.Add((8,-1),0)
        btnsizer.Add(btn2,0,wx.CENTER)
        btnsizer.Add((8,-1),0)
        btnsizer.Add(btn3,0,wx.RIGHT)
        btnsizer.Layout()
        w = btn1.GetSize()[0]+btn2.GetSize()[0]+btn3.GetSize()[0]+btnExp.GetSize()[0]+btnImp.GetSize()[0]+btnImpSR.GetSize()[0]+5*8
        w1 = btnUP.GetSize()[0]+8

        onRefresh()

        self.grid.SetMinSize((w-w1,-1))
        szr = wx.BoxSizer(wx.VERTICAL)
        sizer = wx.GridBagSizer(1,5)
        sizer.Add(wx.StaticText(self, -1, self.text.lblList),(0,0),(1,2))
        sizer.Add(self.grid, (1,0), (7, 2), wx.EXPAND, 5)
        sizer.Add(btnUP, (1,2), (1, 1),flag=wx.RIGHT)
        sizer.Add(btnDOWN, (2,2), (1, 1),flag=wx.RIGHT)
        sizer.Add(btnDEL, (3,2), (1, 1),flag=wx.RIGHT)
        sizer.Add((5,20), (4,2), (1, 1),flag=wx.RIGHT)
        sizer.Add(btnRefr, (5,2), (1, 1),flag=wx.RIGHT)
        sizer.Add(btnSort, (6,2), (1, 1),flag=wx.RIGHT)


        sizer.Add(sourceLabel, (8,0), (1, 1),wx.TOP, 10)
        sizer.Add(sourceCtrl, (8,1), (1, 2), wx.EXPAND|wx.TOP, 5)
        sizer.Add(genreLabel, (9,0), (1, 1),wx.TOP, 10)
        sizer.Add(genreCtrl, (9,1), (1, 2), wx.EXPAND|wx.TOP, 5)
        sizer.Add(langLabel, (10,0), (1, 1),wx.TOP, 10)
        sizer.Add(langCtrl, (10,1), (1, 2), wx.EXPAND|wx.TOP, 5)
        sizer.Add(countryLabel, (11,0), (1, 1),wx.TOP, 10)
        sizer.Add(countryCtrl, (11,1), (1, 2), wx.EXPAND|wx.TOP, 5)
        sizer.AddGrowableCol(1)
        sizer.AddGrowableRow(7)
        szr.Add(sizer, 1, wx.EXPAND|wx.ALL, 5)
        szr.Add(line, 0, wx.EXPAND|wx.TOP, 3)
        szr.Add(btnsizer, 0, wx.EXPAND|wx.ALL, 5)
        self.SetSizer(szr)
        self.Fit()

        #Learn New MINSIZE:
        #====================
        if ConfigData.plcmnt:
    #    if 0:
            self.SetPosition(ConfigData.plcmnt[0])
            sz = ConfigData.plcmnt[1]
            minsz = ConfigData.plcmnt[2]
        else:
            self.Center()
            sz = (w+w1, self.GetSize()[1] + btn1.GetSize()[1] + 10)
            minsz = sz
        self.SetMinSize(minsz)
        self.SetSize(sz)
        self.Show(True)


        def onSource(evt):
            if self.grid.GetSelectedItemCount() == 1:
                self.tmpFavs[self.grid.GetFirstSelected()][0] = evt.GetString()
                self.Diff()
        sourceCtrl.Bind(wx.EVT_CHOICE, onSource)


        def Move(evt):
            id = evt.GetId()
            first = self.grid.GetFirstSelected()
            cnt = self.grid.GetSelectedItemCount()
            if id == self.idUp:
                if first:
                    bit = self.tmpFavs.pop(first-1)
                    self.tmpFavs.insert(first-1+cnt, bit)
                else:
                    id = self.idBottom
            elif id == self.idDown:
                if first+cnt < len(self.tmpFavs):
                    bit = self.tmpFavs.pop(first+cnt)
                    self.tmpFavs.insert(first, bit)
                else:
                    id = self.idTop
            if id in (self.idBottom, self.idTop):
                p1=self.tmpFavs[:first]
                p2=self.tmpFavs[first:first+cnt]
                p3=self.tmpFavs[first+cnt:]
                if id == self.idTop:
                    p2.extend(p1)
                    p2.extend(p3)
                    self.tmpFavs = p2
                elif id == self.idBottom:
                    p1.extend(p3)
                    p1.extend(p2)
                    self.tmpFavs = p1
            self.grid.DeleteAllItems()
            for row in range(len(self.tmpFavs)):
                self.grid.InsertStringItem(row, self.tmpFavs[row][1])
            if id == self.idUp:
                if first:
                    b, e = (first-1, first-1+cnt)
            elif id == self.idDown:
                if first+cnt < len(self.tmpFavs):
                    b, e = (first+1,first+1+cnt)
            elif id == self.idBottom:
                ln = len(self.tmpFavs)
                b, e = (ln-cnt, ln)
            elif id == self.idTop:
                b, e = (0, cnt)
            for ix in range(b, e):
                    self.grid.Select(ix, True)
            self.grid.EnsureVisible(ix)
            self.Diff()
            self.Colour()
        btnUP.Bind(wx.EVT_BUTTON, Move)
        btnDOWN.Bind(wx.EVT_BUTTON, Move)


        def onRemDupl(evt):
            indexes=dict(map(None,[item[0] for item in self.tmpFavs],range(len(self.tmpFavs)))).values()
            indexes.sort()
            tmp = []
            for ix in indexes:
                tmp.append(self.tmpFavs[ix])
            onRefresh(None, tmp)
            self.Diff()
            self.Colour()


        def onDelete(evt):
            cnt = self.grid.GetItemCount()
            for ix in range(cnt-1, -1, -1):
                if self.grid.IsSelected(ix):
                    self.grid.DeleteItem(ix)
                    self.tmpFavs.pop(ix)
            EnableCtrls()
            self.Diff()
            self.Colour()
        btnDEL.Bind(wx.EVT_BUTTON, onDelete)


        def onPlayNow(evt):
            ix = self.grid.GetFirstSelected()
            self.plugin.RefreshVariables()
            sel = self.tmpFavs[ix][1]
            src = sourceCtrl.GetStringSelection()
            rsList = [item[1] for item in self.plugin.Favorites]
            hwnds = HandleRS()
            indx = None
            if sel in [item[1] for item in self.plugin.Favorites]:
                indx = rsList.index(sel)
                if src != self.plugin.Favorites[indx][0]:
                    indx = None
            if indx is not None: # start with favorite index
                if not hwnds:
                    hwnds = self.plugin.GetNewHwnd()
                    if hwnds:
                        SendMessage(hwnds[0], WM_COMMAND, 4102+indx, 0)
                    else:
                        self.FailedToOpen()
                else:
                    for hwnd in hwnds:
                        x, rec = self.plugin.GetStatusRS([hwnd])
                        if rec != 1:
                            SendMessage(hwnd, WM_COMMAND, 4102+indx, 0)
                            break
                    if rec or rec is None:
                        hwnds = self.plugin.GetNewHwnd(hwnds)
                        if hwnds:
                            SendMessage(hwnds[0], WM_COMMAND, 4102+indx, 0)
                        else:
                            self.FailedToOpen()
            else: #start with source="blablabla"
                if not hwnds:
                    hwnds = self.plugin.GetNewHwnd(hwnds, src=src)
                    if not hwnds:
                        self.FailedToOpen()
                else:
                    for hwnd in hwnds:
                        x, rec = self.plugin.GetStatusRS([hwnd])
                        if rec != 1:
                            PostMessage(hwnd, WM_COMMAND, 1, 0) #close
                            i = 0
                            while hwnd in hwnds and i < 100:
                                hwnds = HandleRS()
                                i += 1
                            if i == 100:
                                self.PrintError(self.text.message6)
                                rec = 1
                            else:
                                hwnds = self.plugin.GetNewHwnd(hwnds, src=src)
                                if not hwnds:
                                    self.FailedToOpen()
                                    rec = 1
                                else:
                                    break
                    if rec or rec is None:
                        hwnds = self.plugin.GetNewHwnd(hwnds, src=src)
                        if not hwnds:
                            self.FailedToOpen()
        self.grid.Bind(wx.EVT_LIST_ITEM_ACTIVATED, onPlayNow)


        def AreDuplications():
            srcList = [item[0] for item in self.tmpFavs]
            return len(srcList) > len(set(srcList))


        def OnRightClick(evt):
            if not hasattr(self, "popupID1"):
                self.popupID1 = wx.NewId()
                self.popupID2 = wx.NewId()
                self.Bind(wx.EVT_MENU, onDelete, id = self.popupID1)
                self.Bind(wx.EVT_MENU, onRemDupl, id = self.popupID2)
                self.Bind(wx.EVT_MENU, Move, id = self.idUp)
                self.Bind(wx.EVT_MENU, Move, id = self.idDown)
                self.Bind(wx.EVT_MENU, Move, id = self.idTop)
                self.Bind(wx.EVT_MENU, Move, id = self.idBottom)
                self.Bind(wx.EVT_MENU, onPlayNow, id = self.idPlay)
                self.Bind(wx.EVT_MENU, onSort, id = self.idSort)
                self.Bind(wx.EVT_MENU, onRefresh, id = self.idRefr)
                self.Bind(wx.EVT_MENU, self.onBtnsInOut, id = wx.ID_SAVEAS)
                self.Bind(wx.EVT_MENU, self.onBtnsInOut, id = wx.ID_SAVE)
                self.Bind(wx.EVT_MENU, self.onBtnsInOut, id = wx.ID_OPEN)
                self.Bind(wx.EVT_MENU, self.onBtnsInOut, id = wx.ID_FILE)
            menu = wx.Menu()
            if self.grid.GetSelectedItemCount() == 1:
                menu.Append(self.idPlay, self.text.play)
                menu.AppendSeparator()
            menu.Append(self.popupID1, self.text.toolTipDelete)
            if AreDuplications():
                menu.Append(self.popupID2, self.text.removeDupl)
            if self.grid.GetItemCount() > 1:
                menu.Append(self.idSort, self.text.sort)
            if self.menuFlagM:
                menu.AppendSeparator()
                menu.Append(self.idUp, self.text.toolTipUp)
                menu.Append(self.idDown, self.text.toolTipDown)
                menu.Append(self.idTop, self.text.moveTop)
                menu.Append(self.idBottom, self.text.moveBottom)
            menu.AppendSeparator()
            menu.Append(self.idRefr, self.text.refresh)
            menu.Append(wx.ID_SAVEAS, self.text.exportSel)
            menu.Append(wx.ID_SAVE, self.text.exportAll)
            menu.Append(wx.ID_OPEN, self.text.toolTipImport)
            menu.Append(wx.ID_FILE, self.text.toolTipImportSR)
            self.PopupMenu(menu)
            menu.Destroy()
            evt.Skip()
        self.grid.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, OnRightClick)


    def FailedToOpen(self):
        return MessageBox(
            self,
            self.text.message9, #failed to open
            self.text.messBoxTit6,
            wx.ICON_EXCLAMATION,
            15,
            plugin = self.plugin,
            )


    def CreateFavorites(self, dom, node, itmList = None, save = False):
        max = self.plugin.maxFav
        mssgs = []
        if save:
            #Duplications check
            indexes = dict(map(None,[item[0] for item in self.tmpFavs],range(len(self.tmpFavs)))).values()
            indexes.sort()
            tmp = []
            for ix in indexes:
                tmp.append(self.tmpFavs[ix])
            itmList = range(len(tmp))
            if len(self.tmpFavs) > len(tmp):
                mssgs.append(self.text.message8)
        else:
            tmp = self.tmpFavs
        flag = save and len(itmList) > max
        if flag:
            mssgs.append(self.text.message1 % self.plugin.maxFav)
        if mssgs:
            MessageBox(
                self,
                "\n".join(mssgs),
                self.plugin.text.messBoxTit1,
                wx.ICON_EXCLAMATION,
                plugin = self.plugin,
                )
        elm = 0
        for i in itmList:
            elm += 1
            if flag and elm > max:
                break
            item = tmp[i]
            itemNode = dom.createElement(u'Item-%i' % elm)
            sourceNode = dom.createElement(u'Source')
            sourceText = dom.createTextNode(unicode(item[0]))
            sourceNode.appendChild(sourceText)
            itemNode.appendChild(sourceNode)
            titleNode = dom.createElement(u'Title')
            titleText = dom.createTextNode(unicode(item[1]))
            titleNode.appendChild(titleText)
            itemNode.appendChild(titleNode)
            genreNode = dom.createElement(u'Genre')
            genreText = dom.createTextNode(unicode(item[2]))
            genreNode.appendChild(genreText)
            itemNode.appendChild(genreNode)
            languageNode = dom.createElement(u'Language')
            languageText = dom.createTextNode(unicode(item[3]))
            languageNode.appendChild(languageText)
            itemNode.appendChild(languageNode)
            countryNode = dom.createElement(u'Country')
            countryText = dom.createTextNode(unicode(item[4]))
            countryNode.appendChild(countryText)
            itemNode.appendChild(countryNode)
            node.appendChild(itemNode)


    def UpdateRadioSureXml(self):
        # create a backup of original file
        new_file_name = u'%s\\RadioSure.xml' % self.plugin.xmlpath
        old_file_name = new_file_name + "~"
        if exists(old_file_name):
            remove(old_file_name)
        rename(new_file_name, old_file_name)
        try:
            # change Favorites node
            doc = miniDom.parse(old_file_name)
            node = doc.getElementsByTagName('XMLConfigSettings')[0]
            oldFavorites = node.getElementsByTagName('Favorites')[0]
            newFavorites = doc.createElement(u'Favorites')
            self.CreateFavorites(doc, newFavorites, save = True)
            node.replaceChild(newFavorites, oldFavorites)
            # persist changes to new file
            f = file(new_file_name, "wb")
            writer = lookup('utf-8')[3](f)
            doc.writexml(writer, encoding = 'utf-8')
            f.close()
            MessageBox(
                self,
                self.text.message5, #updated
                self.text.messBoxTit5,
                wx.ICON_INFORMATION,
                15,
                plugin = self.plugin,
                )
            return True
        except:
            raise
            MessageBox(
                self,
                self.text.message3,
                self.plugin.text.messBoxTit1,
                wx.ICON_EXCLAMATION,
                plugin = self.plugin,
                )
            if exists(new_file_name):
                remove(new_file_name)
            rename(old_file_name, new_file_name)
            return False

    def onBtn(self, evt):

        def UpdateXml():
            closeFlag = self.UpdateRadioSureXml()
            rs = u'%s\\RadioSure.exe' % self.plugin.RadioSurePath
            rs = rs.encode(FSE) if isinstance(rs, unicode) else rs
            args = [rs]
            if isfile(rs):
                Popen(args)
            return closeFlag

        closeFlag = False
        id = evt.GetId()
        if id == wx.ID_APPLY or (id == wx.ID_OK and self.favs != self.tmpFavs):
            hwnds = HandleRS()
            rec = 0
            for hwnd in hwnds:
                rec = self.plugin.GetStatusRS([hwnd])[1]
                if rec:
                    break
            title = self.text.messBoxTit3 if rec else self.text.messBoxTit2

            if hwnds:
                # RS is running !
                res = MessageBox(
                    self,
                    self.text.message2 % (self.plugin.text.yes, self.plugin.text.no),
                    title,
                    wx.ICON_EXCLAMATION|wx.YES_NO|wx.YES_DEFAULT,
                    plugin = self.plugin,
                    )
                if res == wx.ID_YES:
                    for hwnd in hwnds:
                        rec = self.plugin.GetStatusRS([hwnd])[1]
                        if rec:
                            PostMessage(hwnd, WM_COMMAND, 1051, 0) # Stop Rec
                            i=0
                            while rec and i < 100:
                                i+=1
                                rec = self.plugin.GetStatusRS([hwnd])[1]
                                sleep(0.1)
                            if not rec:
                                PostMessage(hwnd, WM_COMMAND, 1, 0) # Close
                        else:
                            PostMessage(hwnd, WM_COMMAND, 1, 0) # Close
                    i = 0
                    while hwnds and i < 100:
                        i += 1
                        hwnds = HandleRS()
                    if hwnds:
                        pid = eg.WinApi.Utils.PyGetWindowThreadProcessId(hwnd)[1]
                        handle = _kernel32.OpenProcess(PROCESS_TERMINATE, False, pid)
                        succ = _kernel32.TerminateProcess(handle, -1)
                        _kernel32.CloseHandle(handle)
                        if not succ:
                            MessageBox(
                                self,
                                self.text.message6, #failed to close
                                self.text.messBoxTit6,
                                wx.ICON_EXCLAMATION,
                                15,
                                plugin = self.plugin,
                                )
                        else:
                            closeFlag = UpdateXml()
                    else:
                        closeFlag = UpdateXml()
                else:
                    MessageBox(
                        self,
                        self.text.message7, #no update
                        self.text.messBoxTit7,
                        wx.ICON_EXCLAMATION,
                        15,
                        plugin = self.plugin,
                        )
            else:
                closeFlag = self.UpdateRadioSureXml()

        if id == wx.ID_APPLY and closeFlag:
            self.favs = cpy(self.tmpFavs)
            self.Diff()

        if id != wx.ID_APPLY:
            if id != wx.ID_OK or closeFlag or self.favs == self.tmpFavs:
                self.Close()
        #evt.Skip()


    def Import(self, data):
        # ToDo: Add check of duplications ???
        self.tmpFavs.extend(data)
        self.grid.DeleteAllItems()
        for row in range(len(self.tmpFavs)):
            self.grid.InsertStringItem(row, self.tmpFavs[row][1])
        self.grid.SetColumnWidth(0, -1)
        self.grid.SetColumnWidth(0, self.grid.GetColumnWidth(0) + 6)
        self.grid.EnsureVisible(len(self.tmpFavs)-1)
        self.grid.SetFocus()
        self.Colour()
        self.Diff()


    def Colour(self):
        maxF = self.plugin.maxFav
        cnt = self.grid.GetItemCount()
        fore = self.grid.GetTextColour()
        for row in range(min(maxF, cnt)):
            item = self.grid.GetItem(row)
            item.SetTextColour(fore)
            self.grid.SetItem(item)
        if maxF >= cnt:
            return
        for row in range(maxF, cnt):
            item = self.grid.GetItem(row)
            item.SetTextColour("red")
            self.grid.SetItem(item)


    def onBtnsInOut(self, evt):
        id = evt.GetId()
        if id == wx.ID_SAVEAS or id == wx.ID_SAVE:
            dlg = wx.FileDialog(
                self,
                message = self.text.save,
                defaultDir = self.plugin.xmlpath,
                defaultFile = "Favorites.xml",
                wildcard = self.text.wildcard,
                style=wx.SAVE
                )
            if dlg.ShowModal() == wx.ID_OK:
                self.Export(dlg.GetPath(), id)
            dlg.Destroy()
        elif id == wx.ID_OPEN: # Import
            dlg = wx.FileDialog(
                self,
                message = self.text.choose,
                defaultDir = self.plugin.xmlpath,
                defaultFile = "*.xml",
                wildcard = self.text.wildcard,
                style = wx.OPEN | wx.CHANGE_DIR
                )
            flg = True
            filePath = None
            if dlg.ShowModal() == wx.ID_OK:
                filePath = dlg.GetPath()
                dlg.Destroy()
                xmldoc = miniDom.parse(filePath)
                document = xmldoc.getElementsByTagName('Favorites')
                if len(document) > 0:
                    stations = getStations(document[0])
                    if stations:
                        flg = False
                        self.Import(stations)
            if flg and filePath:
                MessageBox(
                    self,
                    self.text.message4 % split(filePath)[1],
                    self.plugin.text.messBoxTit1,
                    wx.ICON_EXCLAMATION,
                    plugin = self.plugin,
                    )
        elif id == wx.ID_FILE: # Import SR
            dlg = wx.FileDialog(
                self,
                message = self.text.choose,
                defaultDir = eg.folderPath.ProgramFiles+'\\Screamer',
                defaultFile = "favorites.xml",
                wildcard = self.text.wildcard,
                style = wx.OPEN | wx.CHANGE_DIR
                )
            if dlg.ShowModal() == wx.ID_OK:
                filePath = dlg.GetPath()
                dlg.Destroy()
                stations = self.ImportSR(filePath)
                if not stations:
                    MessageBox(
                        self,
                        self.text.message4 % split(filePath)[1],
                        self.plugin.text.messBoxTit1,
                        wx.ICON_EXCLAMATION,
                        plugin = self.plugin,
                        )
                else:
                    self.Import(stations)
        evt.Skip()
        return


    def Diff(self):
        wx.FindWindowById(wx.ID_APPLY).Enable(self.favs != self.tmpFavs)


    def onClose(self, evt):
        hwnd = self.GetHandle()
        wp = GetWindowPlacement(hwnd)[4]
        cdr = wx.GetClientDisplayRect()
        #Note: GetPosition() return (-32000, -32000), if window is minimized !!!
        plcmnt = (
            (wp[0] + cdr[0], wp[1] + cdr[1]),                            # pos
            (wp[2] - wp[0], wp[3] - wp[1]),                              # size
            (self.GetMinSize().GetWidth(),self.GetMinSize().GetHeight()) # min size
        )
        if plcmnt != ConfigData.plcmnt:
            ConfigData.plcmnt = plcmnt
            #if not eg.document.IsDirty():
            #    wx.CallAfter(eg.Notify, "DocumentChange", True)
        self.plugin.manager = None
        wx.CallAfter(self.Show, False)
        wx.CallAfter(self.Destroy)
        evt.Skip()


    def ImportSR(self, filePath):
        xmldoc = miniDom.parse(filePath)
        document = xmldoc.getElementsByTagName('Screamer')
        if len(document) > 0:
            res = []
            stations = tuple(document[0].getElementsByTagName('Station'))
            for station in stations:
                if "title" in station.attributes.keys():
                    title = station.attributes["title"].value
                else:
                    return None
                src = station.getElementsByTagName('Source')
                if len(src)>0:
                    src = src[0].firstChild.data
                    i = -1
                    for ix in range(5, 11):
                        srcIx = [itm[ix] for itm in self.data]
                        if src in srcIx:
                            i = srcIx.index(src)
                            break
                    if i > -1:
                        station = self.data[i]
                        itm = (src, station[0], station[2], station[4], station[3])
                    else:
                        itm = (src, title, "-", "-", "-")
                    res.append(itm)
                else:
                    return None
            return res
        return None


    def Export(self, path, id):
        impl = miniDom.getDOMImplementation()
        dom = impl.createDocument(None, u'XMLConfigSettings', None)
        root = dom.documentElement
        commentNode = dom.createComment(self.text.xmlComment1)
        dom.insertBefore(commentNode, root)
        commentNode = dom.createComment(self.text.xmlComment2 % str(dt.now())[:19])
        dom.insertBefore(commentNode, root)
        favNode = dom.createElement(u'Favorites')
        root.appendChild(favNode)
        if id == wx.ID_SAVEAS and self.grid.GetSelectedItemCount():
            itmList = [itm for itm in range(len(self.tmpFavs)) if self.grid.IsSelected(itm)]
        else:
            itmList = range(len(self.tmpFavs))
        self.CreateFavorites(dom, favNode, itmList)
        f = file(path, 'wb')
        writer = lookup('utf-8')[3](f)
        dom.writexml(writer, encoding = 'utf-8')
        f.close()
#===============================================================================

class SchedulerDialog(wx.Dialog):
    lastRow = -1
    applyBttn = None

    def __init__(self, text, plugin):
        wx.Dialog.__init__(
            self,
            None,
            -1,
            text.dialogTitle % version,
            style = wx.DEFAULT_DIALOG_STYLE|wx.MINIMIZE_BOX|wx.CLOSE_BOX,
        )

        #import locale as l
        #l.setlocale(l.LC_ALL, "us") # only for testing

        bttns = []
        self.ctrls=[]
        self.plugin = plugin
        self.SetIcon(self.plugin.info.icon.GetWxIcon())
        self.plugin.dialog = self
        self.tmpData = self.plugin.tmpData = cpy(self.plugin.data)
        self.text = text

        def fillDynamicSizer(type, data = None, old_type = 255):
            flag = old_type != type
            if flag:
                dynamicSizer.Clear(True)
                self.ctrls=[]
                self.ctrls.append(wx.NewId())
                self.ctrls.append(wx.NewId())
            if type == -1:
                return
            if type != 1 and flag:
                topSizer = wx.StaticBoxSizer(
                    wx.StaticBox(self, -1, self.text.chooseDay),
                    wx.HORIZONTAL
                )
            if type == 0:
                if flag:
                    self.ctrls.append(wx.NewId())
                    dp = wx.DatePickerCtrl(self, self.ctrls[2], size = (86, -1),
                            style = wx.DP_DROPDOWN | wx.DP_SHOWCENTURY)
                    topSizer.Add(dp,0,wx.EXPAND)
                    self.ctrls.append(wx.NewId())
                    yearlyCtrl = wx.CheckBox(self, self.ctrls[3], self.text.yearly)
                    topSizer.Add(yearlyCtrl, 0, wx.EXPAND|wx.LEFT, 30)
                    dynamicSizer.Add(topSizer, 0, wx.EXPAND|wx.TOP, 2)
                else:
                    dp = wx.FindWindowById(self.ctrls[2])
                    yearlyCtrl = wx.FindWindowById(self.ctrls[3])
                if data:
                    if not data[2]:
                        val = wx.DateTime_Now()
                        data[2] = str(dt.now())[:10]
                    wxDttm = wx.DateTime()
                    wxDttm.Set(
                        int(data[2][8:10]),
                        int(data[2][5:7]) - 1,
                        int(data[2][:4])
                    )
                    dp.SetValue(wxDttm)
                    yearlyCtrl.SetValue(data[3])
            elif type == 2:
                if flag:
                    if self.plugin.first_day:
                        choices = list(day_name)[:-1]
                        choices.insert(0, list(day_name)[-1])
                    else:
                        choices = list(day_name)
                    self.ctrls.append(wx.NewId())
                    weekdayCtrl = wx.CheckListBox(
                        self,
                        self.ctrls[2],
                        choices = choices,
                        size=((-1,110)),
                    )
                    self.ctrls.append(wx.NewId())
                    holidCheck_2 = wx.CheckBox(
                        self,
                        self.ctrls[3],
                        self.text.holidCheck_2
                    )
                    self.ctrls.append(wx.NewId())
                    holidCheck_1 = wx.CheckBox(
                        self,
                        self.ctrls[4],
                        self.text.holidCheck_1
                    )
                    topSizer.Add((40,1), 0, wx.ALIGN_CENTER)
                    topSizer.Add(
                        wx.StaticText(
                            self,
                            -1,
                            self.text.theEvery
                        ),
                        0,
                        wx.ALIGN_CENTER | wx.RIGHT, 10
                    )
                    topSizer.Add(weekdayCtrl, 0, wx.TOP)
                    dynamicSizer.Add(topSizer, 0, wx.EXPAND | wx.TOP,2)
                    dynamicSizer.Add(holidCheck_1, 0, wx.TOP, 2)
                    dynamicSizer.Add(holidCheck_2, 0, wx.TOP, 2)
                else:
                    weekdayCtrl = wx.FindWindowById(self.ctrls[2])
                    holidCheck_2 = wx.FindWindowById(self.ctrls[3])
                    holidCheck_1 = wx.FindWindowById(self.ctrls[4])
                val = 127 if not data else data[2]
                if self.plugin.first_day:
                    exp = [6, 0, 1, 2, 3, 4, 5]
                else:
                    exp = [0, 1, 2, 3, 4, 5, 6]
                for i in range(7):
                    weekdayCtrl.Check(i, bool(val & (2 ** exp[i])))
                enable = val & 31 and not val & 96
                holidCheck_1.Enable(enable)
                check = 0 if (not data or not enable) else data[4]
                holidCheck_1.SetValue(check)
                enable = val & 96 and not val & 31
                holidCheck_2.Enable(enable)
                check = 0 if (not data or not enable) else data[3]
                holidCheck_2.SetValue(check)
            elif type == 3: # Monthly/weekday ...
                if flag:
                    dateSizer = wx.BoxSizer(wx.HORIZONTAL)
                    dateSizer.Add(
                        wx.StaticText(
                            self,
                            -1,
                            self.text.the
                        ),
                        0,
                        wx.ALIGN_CENTER
                    )
                    topSizer.Add(dateSizer, 0, wx.EXPAND)
                    dynamicSizer.Add(topSizer, 0, wx.EXPAND | wx.TOP,2)
                    self.ctrls.append(wx.NewId())
                    serialCtrl = wx.CheckListBox(
                        self,
                        self.ctrls[2],
                        choices = self.text.serial_num,
                        size = ((-1, 95)),
                    )
                    dateSizer.Add(serialCtrl, 0, wx.ALIGN_CENTER | wx.LEFT, 10)
                    if self.plugin.first_day:
                        choices = list(day_name)[0:-1]
                        choices.insert(0, list(day_name)[-1])
                    else:
                        choices = list(day_name)
                    self.ctrls.append(wx.NewId())
                    weekdayCtrl = wx.CheckListBox(
                        self,
                        self.ctrls[3],
                        choices = choices,
                        size = ((-1, 110)),
                    )
                    dateSizer.Add(weekdayCtrl, 0, wx.ALIGN_CENTER | wx.LEFT, 10)
                    dateSizer.Add(
                        wx.StaticText(
                            self,
                            -1,
                            self.text.in_
                        ),
                        0,
                        wx.ALIGN_CENTER | wx.LEFT, 10
                    )
                    self.ctrls.append(wx.NewId())
                    monthsCtrl_1 = wx.CheckListBox(
                        self,
                        self.ctrls[4],
                        choices = list(month_name)[1:7],
                        size = ((-1, 95)),
                    )
                    dateSizer.Add(monthsCtrl_1, 0, wx.ALIGN_CENTER | wx.LEFT, 10)
                    self.ctrls.append(wx.NewId())
                    monthsCtrl_2 = wx.CheckListBox(
                        self,
                        self.ctrls[5],
                        choices = list(month_name)[7:],
                        size = ((-1, 95)),
                    )
                    dateSizer.Add(monthsCtrl_2, 0, wx.ALIGN_CENTER | wx.LEFT, -1)
                    self.ctrls.append(wx.NewId())
                    holidCheck_1 = wx.CheckBox(
                        self,
                        self.ctrls[6],
                        self.text.holidCheck_1
                    )
                    dynamicSizer.Add(holidCheck_1, 0, wx.TOP, 2)
                else:
                    serialCtrl = wx.FindWindowById(self.ctrls[2])
                    weekdayCtrl = wx.FindWindowById(self.ctrls[3])
                    monthsCtrl_1 = wx.FindWindowById(self.ctrls[4])
                    monthsCtrl_2 = wx.FindWindowById(self.ctrls[5])
                    holidCheck_1 = wx.FindWindowById(self.ctrls[6])
                val = 0 if not data else data[2]
                for i in range(6):
                    serialCtrl.Check(i, bool(val & (2 ** i)))
                val = 0 if not data else data[3]
                if self.plugin.first_day:
                    exp = [6, 0, 1, 2, 3, 4, 5]
                else:
                    exp = [0, 1, 2, 3, 4, 5, 6]
                for i in range(7):
                    weekdayCtrl.Check(i, bool(val & (2 ** exp[i])))
                enable = val & 31 and not val & 96
                holidCheck_1.Enable(enable)
                val = 63 if not data else data[4]
                for i in range(6):
                    monthsCtrl_1.Check(i, bool(val & (2 ** i)))
                val = 63 if not data else data[5]
                for i in range(6):
                    monthsCtrl_2.Check(i, bool(val & (2 ** i)))
                check = 0 if (not data or not enable) else data[6]
                holidCheck_1.SetValue(check)
            elif type == 4: # Monthly/day ...
                if flag:
                    dateSizer = wx.BoxSizer(wx.HORIZONTAL)
                    topSizer.Add(dateSizer, 0, wx.EXPAND)
                    dynamicSizer.Add(topSizer, 0, wx.EXPAND | wx.TOP, 2)
                    self.ctrls.append(wx.NewId())
                    q_1_Ctrl = wx.CheckListBox(
                        self,
                        self.ctrls[2],
                        choices = [str(i) + '.' for i in range(1, 9)],
                        size = ((40, 125)),
                    )
                    dateSizer.Add(q_1_Ctrl, 0, wx.LEFT, 5)
                    self.ctrls.append(wx.NewId())
                    q_2_Ctrl = wx.CheckListBox(
                        self,
                        self.ctrls[3],
                        choices = [str(i) + '.' for i in range(9, 17)],
                        size = ((46, 125)),
                    )
                    dateSizer.Add(q_2_Ctrl, 0, wx.LEFT, -1)
                    self.ctrls.append(wx.NewId())
                    q_3_Ctrl = wx.CheckListBox(
                        self,
                        self.ctrls[4],
                        choices = [str(i) + '.' for i in range(17, 25)],
                        size = ((46, 125)),
                    )
                    dateSizer.Add(q_3_Ctrl, 0, wx.LEFT, -1)
                    self.ctrls.append(wx.NewId())
                    q_4_Ctrl = wx.CheckListBox(
                        self,
                        self.ctrls[5],
                        choices = [str(i) + '.' for i in range(25, 32)],
                        size = ((46, 125)),
                    )
                    dateSizer.Add(q_4_Ctrl, 0, wx.LEFT, -1)
                    dateSizer.Add((-1, 1), 1, wx.EXPAND)
                    self.ctrls.append(wx.NewId())
                    monthsCtrl_1 = wx.CheckListBox(
                        self,
                        self.ctrls[6],
                        choices = list(month_name)[1:7],
                        size = ((-1, 95)),
                    )
                    dateSizer.Add(monthsCtrl_1, 0, wx.ALIGN_CENTER | wx.LEFT, 10)
                    self.ctrls.append(wx.NewId())
                    monthsCtrl_2 = wx.CheckListBox(
                        self,
                        self.ctrls[7],
                        choices = list(month_name)[7:],
                        size = ((-1, 95)),
                    )
                    dateSizer.Add(monthsCtrl_2, 0, wx.ALIGN_CENTER | wx.LEFT, -1)
                    dateSizer.Add((5, 1), 0)
                else:
                    q_1_Ctrl = wx.FindWindowById(self.ctrls[2])
                    q_2_Ctrl = wx.FindWindowById(self.ctrls[3])
                    q_3_Ctrl = wx.FindWindowById(self.ctrls[4])
                    q_4_Ctrl = wx.FindWindowById(self.ctrls[5])
                    monthsCtrl_1 = wx.FindWindowById(self.ctrls[6])
                    monthsCtrl_2 = wx.FindWindowById(self.ctrls[7])
                val = 0 if not data else data[2]
                for i in range(8):
                    q_1_Ctrl.Check(i, bool(val & (2 ** i)))
                val = 0 if not data else data[3]
                for i in range(8):
                    q_2_Ctrl.Check(i, bool(val & (2 ** i)))
                val = 0 if not data else data[4]
                for i in range(8):
                    q_3_Ctrl.Check(i, bool(val & (2 ** i)))
                val = 0 if not data else data[5]
                for i in range(7):
                    q_4_Ctrl.Check(i, bool(val & (2 ** i)))
                val = 63 if not data else data[6]
                for i in range(6):
                    monthsCtrl_1.Check(i, bool(val & (2 ** i)))
                val = 63 if not data else data[7]
                for i in range(6):
                    monthsCtrl_2.Check(i, bool(val & (2 ** i)))
            elif type == 5:
                if flag:
                    self.ctrls.append(wx.NewId())
                    dp = wx.DatePickerCtrl(self, self.ctrls[2], size = (86, -1),
                            style = wx.DP_DROPDOWN | wx.DP_SHOWCENTURY)
                    topSizer.Add(dp, 0, wx.EXPAND)
                    dynamicSizer.Add(topSizer, 0, wx.EXPAND | wx.TOP, 2)
                else:
                    dp = wx.FindWindowById(self.ctrls[2])
                if data:
                    if not data[2]:
                        val = wx.DateTime_Now()
                        data[2] = str(dt.now())[:10]
                    wxDttm = wx.DateTime()
                    wxDttm.Set(
                        int(data[2][8:10]),
                        int(data[2][5:7])-1,
                        int(data[2][:4])
                    )
                    dp.SetValue(wxDttm)
            #elif type == 1: # daily
            #    pass
            if flag:
                timeSizer = wx.GridBagSizer(0, 0)
                bottomSizer = wx.StaticBoxSizer(
                    wx.StaticBox(self, -1, self.text.chooseTime6 if type == 6 else self.text.chooseTime),
                    wx.HORIZONTAL
                )
                dynamicSizer.Add(bottomSizer, 0, wx.EXPAND | wx.TOP, 16 if type != 2 else 5)
                bottomSizer.Add(timeSizer, 0, wx.EXPAND)
                stEvLbl = wx.StaticText(self, -1, self.text.start)
                timeSizer.Add(stEvLbl, (0, 0), (1, 2))
                durLabel = wx.StaticText(self, -1, self.text.length)
                timeSizer.Add(durLabel, (0, 3), (1, 2))
                spinBtn = wx.SpinButton(
                    self,
                    -1,
                    wx.DefaultPosition,
                    (-1, 22),
                    wx.SP_VERTICAL
                )
                initTime = wx.DateTime_Now()
                initTime.SetSecond(0)
                initTime.AddTS(wx.TimeSpan.Minute())
                val = data[0] if data and data[0] else initTime
                timeCtrl = eg.TimeCtrl(
                    self,
                    self.ctrls[0],
                    val,
                    fmt24hr = True,
                    spinButton = spinBtn
                )
                timeSizer.Add(timeCtrl, (1, 0), (1, 1))
                timeSizer.Add(spinBtn, (1, 1), (1, 1))
                timeSizer.Add((40, -1), (1, 2), (1, 1))
                spinBtn2 = wx.SpinButton(
                    self,
                    -1,
                    wx.DefaultPosition,
                    (-1, 22),
                    wx.SP_VERTICAL
                )
                val = data[1] if data and data[1] else "00:00"
                lenCtrl = eg.TimeCtrl_Duration(
                    self,
                    self.ctrls[1],
                    val,
                    fmt24hr = True,
                    spinButton = spinBtn2,
                    displaySeconds = False
                )
                timeSizer.Add(lenCtrl, (1, 3), (1, 1))
                timeSizer.Add(spinBtn2, (1, 4), (1, 1))
                bottomSizer.Add((-1,-1), 1, wx.EXPAND)
                testBttn = wx.Button(
                    self,
                    -1 if len(bttns) == 0 else bttns[-1],
                    self.text.testButton
                )
                bottomSizer.Add(testBttn, 0, wx.EXPAND | wx.RIGHT)
            else:
                timeCtrl = wx.FindWindowById(self.ctrls[0])
                val = data[0] if data and data[0] else wx.DateTime_Now()
                timeCtrl.SetValue(val)
                lenCtrl = wx.FindWindowById(self.ctrls[1])
                val = data[1] if data and data[1] else "00:00"
                lenCtrl.SetValue(val)
            if type == 5: #periodically
                if flag:
                    bottomSizer = wx.StaticBoxSizer(
                        wx.StaticBox(self, -1, self.text.choosePeriod),
                        wx.HORIZONTAL
                    )
                    self.ctrls.append(wx.NewId())
                    numCtrl = MySpinIntCtrl(self, -1, value = 1, min = 1)
                    numCtrl.SetNumCtrlId(self.ctrls[3])
                    bottomSizer.Add(
                        wx.StaticText(
                            self,
                            -1,
                            self.text.andThenEvery
                        ),
                        0,
                        wx.ALIGN_CENTER
                    )
                    bottomSizer.Add(numCtrl, 0, wx.LEFT, 4)
                    self.ctrls.append(wx.NewId())
                    unitCtrl = wx.Choice(
                        self,
                        self.ctrls[4],
                        choices = self.text.units
                    )
                    bottomSizer.Add(unitCtrl, 0, wx.LEFT, 8)
                    dynamicSizer.Add(bottomSizer, 0, wx.EXPAND|wx.TOP, 16)
                    dynamicSizer.Layout()
                else:
                    numCtrl = wx.FindWindowById(self.ctrls[3])
                    unitCtrl = wx.FindWindowById(self.ctrls[4])
                if data:
                    numCtrl.SetValue(str(data[3]))
                    unitCtrl.SetSelection(data[4])
            elif flag:
                dynamicSizer.Layout()
            if type == 6:
                stEvLbl.Show(False)
                timeCtrl.Show(False)
                spinBtn.Show(False)
            return dynamicSizer.GetMinSize()


        def Diff():
            applyBttn = wx.FindWindowById(bttns[5])
            flg = self.tmpData != self.plugin.data
            applyBttn.Enable(flg)


        def onCheckListBox(evt):
            id = evt.GetId()
            sel = evt.GetSelection()
            box = self.FindWindowById(id)
            ix = self.ctrls.index(id)
            type = self.tmpData[self.lastRow][2]
            cond = (type == 2 and ix == 2) or (type == 3 and ix == 3)
            if cond and self.plugin.first_day:
                exp = (6, 0, 1, 2, 3, 4, 5)[sel]
            else:
                exp = sel
            if box.IsChecked(sel):
                self.tmpData[self.lastRow][3][ix] |= 2 ** exp
            else:
                self.tmpData[self.lastRow][3][ix] &= 255 - 2 ** exp
            if cond:
                holidCheck_1 = wx.FindWindowById(self.ctrls[-1])
                val = self.tmpData[self.lastRow][3][ix]
                flg = val & 31 and not val & 96
                holidCheck_1.Enable(flg)
                if not flg:
                    holidCheck_1.SetValue(0)
                    self.tmpData[self.lastRow][3][-1] = 0
                if type == 2:
                    holidCheck_2 = wx.FindWindowById(self.ctrls[3])
                    val = self.tmpData[self.lastRow][3][2]
                    flg = val & 96 and not val & 31
                    holidCheck_2.Enable(flg)
                    if not flg:
                        holidCheck_2.SetValue(0)
                        self.tmpData[self.lastRow][3][3] = 0
            next = self.plugin.NextRun(
                self.tmpData[self.lastRow][2],
                self.tmpData[self.lastRow][3]
            )
            grid.SetStringItem(self.lastRow, 3, next)
            Diff()


        def OnTimeChange(evt):
            ix = self.ctrls.index(evt.GetId())
            self.tmpData[self.lastRow][3][ix] = evt.GetValue()
            next = self.plugin.NextRun(
                self.tmpData[self.lastRow][2],
                self.tmpData[self.lastRow][3]
            )
            grid.SetStringItem(self.lastRow, 3, next)
            Diff()


        def onPeriodUnit(evt):
            if len(self.ctrls) == 5 and evt.GetId() == self.ctrls[4]:
                self.tmpData[self.lastRow][3][4] = evt.GetSelection()
                next = self.plugin.NextRun(
                    self.tmpData[self.lastRow][2],
                    self.tmpData[self.lastRow][3]
                )
                grid.SetStringItem(self.lastRow, 3, next)
            else:
                evt.Skip()
            Diff()


        def onDatePicker(evt):
            val = str(dt.fromtimestamp(evt.GetDate().GetTicks()))[:10]
            self.tmpData[self.lastRow][3][2] = val
            next = self.plugin.NextRun(
                self.tmpData[self.lastRow][2],
                self.tmpData[self.lastRow][3]
            )
            grid.SetStringItem(self.lastRow, 3, next)
            Diff()


        def onCheckBox(evt):
            val = evt.IsChecked()
            ix = self.ctrls.index(evt.GetId())
            if self.tmpData[self.lastRow][2] == 2 and ix == 3:
                self.tmpData[self.lastRow][3][3] = int(val)
            else:
                self.tmpData[self.lastRow][3][-1] = int(val)
            next = self.plugin.NextRun(
                self.tmpData[self.lastRow][2],
                self.tmpData[self.lastRow][3]
            )
            grid.SetStringItem(self.lastRow, 3, next)
            Diff()


        def OnUpdateDialog(evt):
            if self.lastRow == evt.GetId():
                OpenSchedule()


        def OnSelectCell(evt):
            self.lastRow = evt.m_itemIndex
            OpenSchedule()
            Diff()
            evt.Skip() # necessary !!!


        def enableBttns(value):
            for i in (1, 2):
                bttn = self.FindWindowById(bttns[i])
                bttn.Enable(value)
            Diff()


        def ShowMessageBox(mess):
            MessageBox(
                self,
                mess,
                self.text.boxTitle,
                wx.ICON_EXCLAMATION,
                plugin = self.plugin
                )


        def FindNewTitle(title):
            tmpLst = []
            for item in self.tmpData:
                if item[1].startswith(title + " ("):
                    tmpLst.append(item[1][2 + len(title):])
            if len(tmpLst) == 0:
                return "%s (1)" % title
            tmpLst2 = []
            for item in tmpLst:
                if item[-1] == ")":
                    try:
                        tmpLst2.append(int(item[:-1]))
                    except:
                        pass
            if len(tmpLst2) == 0:
                return "%s (1)" % title
            else:
                return "%s (%i)" % (title, 1 + max(tmpLst2))


        def testValidity(data, test = False):
            mssgs = []
            tempDict = dict([(item[1].strip(), item[2]) for item in data])
            if "" in tempDict.iterkeys():
                mssgs.append(self.text.boxTexts[0])
            if not test and len(tempDict) < len(data):
                mssgs.append(self.text.boxTexts[1])
            if -1 in tempDict.itervalues():
                mssgs.append(self.text.boxTexts[4])
            for item in data:
                val = item[7]
                if (val & 6) == 6: # = Do nothing
                    if not val & 24:
                        if not self.text.boxTexts[3] in mssgs:
                            mssgs.append(self.text.boxTexts[3])
                else: # Not "Do nothing"
                    if not item[5]:
                        if not self.text.boxTexts[2] in mssgs:
                            mssgs.append(self.text.boxTexts[2])
                if item[2] == 5 and item[3][4] < 2:
                    period = item[3][3] * (3600, 86400)[item[3][4]]
                    span = 60 * int(item[3][1][3:]) + 3600 * int(item[3][1][:2])
                    if period <= span:
                        if self.text.boxTexts[5] not in mssgs:
                            mssgs.append(self.text.boxTexts[5])
            flag = len(mssgs) > 0
            if flag:
                ShowMessageBox("\n".join(mssgs))
            return flag


        def addSchedule(evt = None):
            empty = [1, "", -1, [], " ", "", "", 5]
            self.lastRow = len(self.tmpData)
            self.tmpData.append(empty)
            Tidy()
            grid.AppendRow()
            grid.SelRow(self.lastRow)
            if not self.lastRow:
                enableBttns(True)
                EnableCtrls(True)
            Diff()


        def duplSchedule(evt = None):
            lngth = len(self.tmpData)
            item = cpy(self.tmpData[self.lastRow])
            nxt = grid.GetItem(self.lastRow, 3).GetText()
            item[4] = ""
            self.lastRow = lngth
            self.tmpData.append(item)
            newTitle = FindNewTitle(self.tmpData[lngth][1])
            self.tmpData[lngth][1] = newTitle
            grid.AppendRow()
            grid.SelRow(lngth)
            grid.SetStringItem(lngth, 1, newTitle)
            grid.SetStringItem(lngth, 3, nxt)
            OpenSchedule()
            Diff()


        def delSchedule(evt = None):
            self.tmpData.pop(self.lastRow)
            grid.DeleteItem(self.lastRow)
            if len(self.tmpData) > 0:
                if self.lastRow == len(self.tmpData):
                    self.lastRow -= 1
                OpenSchedule()
                grid.SelRow(self.lastRow)
            else:
                self.lastRow = -1
                Tidy()
                EnableCtrls(False)
                enableBttns(False)
            Diff()


        def Move(direction):
            lst = cpy(self.tmpData)
            index = self.lastRow
            max = len(lst)-1
            #Last to first position, other down
            if index == max and direction == 1:
                self.tmpData[1:] = lst[:-1]
                self.tmpData[0] = lst[max]
                index2 = 0
            #First to last position, other up
            elif index == 0 and direction == -1:
                self.tmpData[:-1] = lst[1:]
                self.tmpData[max] = lst[0]
                index2 = max
            else:
                index2 = index + direction
                self.tmpData[index] = lst[index2]
                self.tmpData[index2] = lst[index]
            del lst
            return index2


        def moveUp(evt = None):
            newSel = Move(-1)
            fillGrid(False)
            self.grid.SelRow(newSel)
            Diff()


        def moveDown(evt = None):
            newSel = Move(1)
            fillGrid(False)
            self.grid.SelRow(newSel)
            Diff()


        def onButton(evt):
            id = evt.GetId()
            if id == bttns[0]:   # Add new
                addSchedule()
            elif id == bttns[1]: # Duplicate
                duplSchedule()
            elif id == bttns[2]: # Delete
                delSchedule()
            elif id == bttns[3]: # OK
                if testValidity(self.tmpData):
                    evt.Skip()
                    return
                self.plugin.data = cpy(self.tmpData)
                self.tmpData = []
                self.plugin.dataToXml()
                self.plugin.UpdateEGscheduler()
                self.Close()
            elif id == bttns[4]: # Cancel
                self.tmpData = []
                self.Close()
            elif id == bttns[5]: # Apply
                applyBttn = wx.FindWindowById(bttns[5])
                applyBttn.Enable(False)
                if testValidity(self.tmpData):
                    evt.Skip()
                    return
                self.plugin.data = cpy(self.tmpData)
                self.plugin.dataToXml()
                self.plugin.UpdateEGscheduler()
            evt.Skip()


        def EnableCtrls(value):
            typeChoice.Enable(value)
            schedulerName.Enable(value)
            name_label.Enable(value)
            type_label.Enable(value)
            favorite_label.Enable(value)
            workModeLbl.Enable(value)
            triggEvtLbl.Enable(value)
            windOpenLbl.Enable(value)
            source_label.Enable(value)
            filename_label.Enable(value)
            favChoice.Enable(value)
            sourceCtrl.Enable(value)
            recordCtrl.Enable(value)
            workModeCtrl.Enable(value)
            triggEvtCtrl.Enable(value)
            windOpenCtrl.Enable(value)
            if not value:
                workModeCtrl.SetSelection(-1)
                triggEvtCtrl.SetSelection(-1)
                windOpenCtrl.SetSelection(-1)


        def OpenSchedule():
            schedulerName.ChangeValue(self.tmpData[self.lastRow][1])
            type = self.tmpData[self.lastRow][2]
            fillDynamicSizer(
                type,
                self.tmpData[self.lastRow][3],
                typeChoice.GetSelection()
            )
            typeChoice.SetSelection(type)
            modes = self.tmpData[self.lastRow][7]
            rsMode = (modes>>1)&3
            workModeCtrl.SetSelection(rsMode)
            recordCtrl.GetTextCtrl().ChangeValue(self.tmpData[self.lastRow][6])
            sourceCtrl.SetValue(self.tmpData[self.lastRow][5])
            if rsMode == 3:
                windOpenCtrl.SetSelection(-1)
                windOpenCtrl.Enable(False)
                windOpenLbl.Enable(False)
            else:
                windOpenCtrl.SetSelection(modes&1)
                windOpenCtrl.Enable(True)
                windOpenLbl.Enable(True)
            triggEvtCtrl.SetSelection((modes>>3)&3)


        def Tidy():
            favChoice.SetSelection(-1)
            typeChoice.SetSelection(-1)
            windOpenCtrl.SetSelection(1)
            workModeCtrl.SetSelection(2)
            triggEvtCtrl.SetSelection(0)
            sourceCtrl.ChangeValue("")
            recordCtrl.GetTextCtrl().ChangeValue("")
            schedulerName.ChangeValue("")
            fillDynamicSizer(-1)
            filename_label.Enable(True)
            recordCtrl.Enable(True)


        def onCheckListCtrl(evt):
            index, flag = evt.GetValue()
            if self.tmpData[index][0] != int(flag):
                self.tmpData[index][0] = int(flag)
                Diff()


        def onSchedulerTitle(evt):
            txt = evt.GetString()
            grid.SetStringItem(self.lastRow, 1, txt)
            self.tmpData[self.lastRow][1] = txt
            Diff()


        def onPeriodNumber(evt):
            if len(self.ctrls) == 5 and evt.GetId() == self.ctrls[3]:
                self.tmpData[self.lastRow][3][3] = int(evt.GetString())
                next = self.plugin.NextRun(
                    self.tmpData[self.lastRow][2],
                    self.tmpData[self.lastRow][3]
                )
                grid.SetStringItem(self.lastRow, 3, next)
                Diff()
            else:
                evt.Skip()


        def onTestButton(evt):
            data = self.tmpData[self.lastRow]
            if testValidity([data,], True):
                return
            ticks = mktime(localtime())
            next, cmdline = self.plugin.Execute(data, True)
            next = next[:19] if next else self.plugin.text.none
            self.plugin.updateLogFile(self.text.testRun % (data[1], next))
            if cmdline:
                self.plugin.updateLogFile(self.plugin.text.cmdLine % cmdline.decode(FSE))


        def OnRightClick(evt):
            if not hasattr(self, "popupID1"):
                self.popupID1 = wx.NewId()
                self.popupID2 = wx.NewId()
                self.popupID3 = wx.NewId()
                self.popupID4 = wx.NewId()
                self.popupID5 = wx.NewId()
                self.popupID6 = wx.NewId()
                self.popupID7 = wx.NewId()
                self.Bind(wx.EVT_MENU, addSchedule, id=self.popupID1)
                self.Bind(wx.EVT_MENU, duplSchedule, id=self.popupID2)
                self.Bind(wx.EVT_MENU, delSchedule, id=self.popupID3)
                self.Bind(wx.EVT_MENU, self.EnableAll, id=self.popupID4)
                self.Bind(wx.EVT_MENU, self.DisableAll, id=self.popupID5)
                self.Bind(wx.EVT_MENU, moveUp, id=self.popupID6)
                self.Bind(wx.EVT_MENU, moveDown, id=self.popupID7)
            # make a menu
            menu = wx.Menu()
            menu.Append(self.popupID1, self.text.popup[0])
            menu.Append(self.popupID2, self.text.popup[1])
            menu.Append(self.popupID3, self.text.popup[2])
            menu.AppendSeparator()
            menu.Append(self.popupID4, self.text.popup[3])
            menu.Append(self.popupID5, self.text.popup[4])
            menu.AppendSeparator()
            menu.Append(self.popupID6, self.text.popup[5])
            menu.Append(self.popupID7, self.text.popup[6])
            self.PopupMenu(menu)
            menu.Destroy()
            evt.Skip()


        def fillGrid(flag):
            grid.DeleteAllItems()
            rows = len(self.tmpData)
            if rows > 0:
                for row in range(rows):
                    grid.InsertStringItem(row, "")
                    if self.tmpData[row][0]:
                        grid.CheckItem(row)
                    grid.SetStringItem(row, 1, self.tmpData[row][1])
                    grid.SetStringItem(row, 2, self.tmpData[row][4])
                    next = self.plugin.NextRun(self.tmpData[row][2], self.tmpData[row][3])
                    grid.SetStringItem(row, 3, next)
                if flag:
                    self.lastRow = 0
                    grid.SelRow(0)
                    OpenSchedule()
                    enableBttns(True)
            else:
                EnableCtrls(False)
                grid.DeleteItem(0)

        dynamicSizer = wx.BoxSizer(wx.VERTICAL)
        wDynamic = fillDynamicSizer(3)
        fillDynamicSizer(-1)
        grid = CheckListCtrl(self, text, wDynamic[0])
        self.grid = grid

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(grid, 0, wx.ALL, 5)
        favorite_label = wx.StaticText(self, -1, self.text.favorite)
        workModeLbl = wx.StaticText(self, -1, self.text.workModeLabel)
        workModeCtrl = wx.Choice(self, -1, choices = self.text.workModes)
        triggEvtLbl = wx.StaticText(self, -1, self.text.triggEvtLabel)
        triggEvtCtrl = wx.Choice(self, -1, choices = self.text.triggEvtChoices)
        windOpenLbl = wx.StaticText(self, -1, self.text.windOpenLabel)
        windOpenCtrl = wx.Choice(self, -1, choices = self.text.windOpenChoices)
        source_label = wx.StaticText(self, -1, self.text.source)
        self.favorites = self.plugin.RefreshVariables()
        favChoice = wx.Choice(self, -1, choices = [item[1] for item in self.favorites])
        hgh =  48 + favChoice.GetSize()[1]
        sourceCtrl = wx.TextCtrl(self,-1,"")
        filename_label = wx.StaticText(self, -1, self.text.filename)
        schedulerName = wx.TextCtrl(self, -1, "")
        typeChoice = wx.Choice(self, -1, choices = self.text.sched_type)
        xmltoparse = u'%s\\RadioSure.xml' % self.plugin.xmlpath
        xmltoparse = xmltoparse.encode(FSE) if isinstance(xmltoparse, unicode) else xmltoparse
        xmldoc = miniDom.parse(xmltoparse)
        recordings = xmldoc.getElementsByTagName('Recordings')
        if not recordings:
            folder = u'%s\\RadioSure Recordings' % self.plugin.xmlpath
        else:
            folder = recordings[0].getElementsByTagName('Folder')[0].firstChild.data
        recordCtrl = MyFileBrowseButton(
            self,
            toolTip = self.text.toolTipFile,
            dialogTitle = self.text.browseTitle,
            buttonText = eg.text.General.browse,
            startDirectory = folder
        )
        self.grid.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, OnRightClick)

        def onSource(evt):
            src = evt.GetString()
            srcs = [item[0] for item in self.favorites]
            if src in srcs:
                ix = srcs.index(src)
            else:
                ix = -1
            favChoice.SetSelection(ix)
            self.tmpData[self.lastRow][5] = src
            Diff()
            evt.Skip()
        sourceCtrl.Bind(wx.EVT_TEXT, onSource)


        def onFavChoice(evt):
            sel = evt.GetSelection()
            txt = self.favorites[sel][0]
            sourceCtrl.ChangeValue(txt)
            self.tmpData[self.lastRow][5] = txt
            Diff()
            evt.Skip()
        favChoice.Bind(wx.EVT_CHOICE, onFavChoice)


        def onRecordCtrl(evt):
            txt = evt.GetString()
            self.tmpData[self.lastRow][6] = txt
            Diff()
            evt.Skip()
        recordCtrl.GetTextCtrl().Bind(wx.EVT_TEXT, onRecordCtrl)


        def onTypeChoice(evt):
            type = evt.GetSelection()
            if self.tmpData[self.lastRow][2] != type:
                empty_data = [
                    ["", "", 0, 0],
                    ["", ""],
                    ["", "", 127, 0, 0],
                    ["", "", 0, 0, 63, 63, 0],
                    ["", "", 0, 0, 0, 0, 63, 63],
                    ["", "", 0, 1, 0],
                ]
                self.tmpData[self.lastRow][2] = type
                data = empty_data[self.tmpData[self.lastRow][2]]
                self.tmpData[self.lastRow][3] = data
                fillDynamicSizer(type, data)
            Diff()


        def onWorkMode(evt):
            sel = evt.GetSelection()
            if sel == 3:
                windOpenCtrl.SetSelection(-1)
                windOpenCtrl.Enable(False)
                windOpenLbl.Enable(False)
                if triggEvtCtrl.GetSelection() == 0:
                    ShowMessageBox(self.text.boxTexts[3])
            else:
                if windOpenCtrl.GetSelection() == -1:
                    windOpenCtrl.SetSelection(1)
                    windOpenCtrl.Enable(True)
                    windOpenLbl.Enable(True)
            val = self.tmpData[self.lastRow][7]
            val &= (255-6)
            val |= (sel<<1)
            self.tmpData[self.lastRow][7] = val
            Diff()
        workModeCtrl.Bind(wx.EVT_CHOICE, onWorkMode)


        def onWindOpen(evt):
            sel = evt.GetSelection()
            val = self.tmpData[self.lastRow][7]
            val &= (255-1)
            val |= sel
            self.tmpData[self.lastRow][7] = val
            Diff()
        windOpenCtrl.Bind(wx.EVT_CHOICE, onWindOpen)


        def onTriggEvtCtrl(evt):
            sel = evt.GetSelection()
            workMode = workModeCtrl.GetSelection()
            if sel == 0 and workMode == 3:
                ShowMessageBox(self.text.boxTexts[3])
            val = self.tmpData[self.lastRow][7]
            val &= (255-24)
            val |= (sel<<3)
            self.tmpData[self.lastRow][7] = val
            Diff()
        triggEvtCtrl.Bind(wx.EVT_CHOICE, onTriggEvtCtrl)

        bttnSizer = wx.BoxSizer(wx.HORIZONTAL)
        bttnSizer.Add((5, -1))
        i = 0
        for bttn in self.text.buttons:
            id = wx.NewId()
            bttns.append(id)
            b = wx.Button(self, id, bttn)
            bttnSizer.Add(b,1)
            if i in (1, 2, 5):
                b.Enable(False)
            if i == 3:
                b.SetDefault()
            if i == 5:
                self.applyBttn = b
            b.Bind(wx.EVT_BUTTON, onButton, id = id)
            bttnSizer.Add((5, -1))
            i += 1
        sizer.Add(bttnSizer,0,wx.EXPAND)
        id = wx.NewId() #testBttn
        bttns.append(id)
        self.Bind(wx.EVT_BUTTON, onTestButton, id = id)
        wx.EVT_CHECKLISTBOX(self, -1, onCheckListBox)
        EVT_TIMEUPDATE(self, -1, OnTimeChange)
        wx.EVT_TEXT(self, -1, onPeriodNumber)
        wx.EVT_CHOICE(self, -1, onPeriodUnit)
        wx.EVT_DATE_CHANGED(self, -1, onDatePicker)
        wx.EVT_CHECKBOX(self, -1, onCheckBox)
        self.Bind(eg.EVT_VALUE_CHANGED, OnUpdateDialog)
        self.Bind(wx.EVT_LIST_ITEM_SELECTED, OnSelectCell)
        typeChoice.Bind(wx.EVT_CHOICE, onTypeChoice)
        schedulerName.Bind(wx.EVT_TEXT, onSchedulerTitle)
        self.grid.Bind(eg.EVT_VALUE_CHANGED, onCheckListCtrl)
        nameSizer = wx.FlexGridSizer(2, 0, 0, 20)
        nameSizer.AddGrowableCol(0,1)
        name_label = wx.StaticText(self, -1, self.text.header[1] + ":")
        nameSizer.Add(name_label)
        type_label = wx.StaticText(self, -1, self.text.type_label)
        nameSizer.Add(type_label)
        nameSizer.Add(schedulerName, 0, wx.EXPAND)
        nameSizer.Add(typeChoice)
        typeSizer = wx.StaticBoxSizer(
            wx.StaticBox(self, -1, ""),
            wx.VERTICAL
        )
        #dynamicSizer.SetMinSize((-1, 226))
        dynamicSizer.SetMinSize((-1, wDynamic[1]))
        typeSizer.Add(nameSizer, 0, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, 5)
        typeSizer.Add(dynamicSizer, 0, wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT, 5)
        sizer.Add(typeSizer, 0, wx.EXPAND | wx.LEFT | wx.RIGHT, 5)
        sizer.Add(source_label, 0, wx.TOP|wx.LEFT, 5)
        sizer.Add(sourceCtrl,0,wx.EXPAND|wx.LEFT|wx.RIGHT, 5)
        sizer.Add((1,4))
        sizer.Add(favorite_label, 0, wx.TOP|wx.LEFT, 5)
        sizer.Add(favChoice,0,wx.EXPAND|wx.LEFT|wx.RIGHT, 5)
        sizer.Add((1,4))
        choicesSizer = wx.FlexGridSizer(2,3,0,10)
        choicesSizer.Add(windOpenLbl,0)
        choicesSizer.Add(workModeLbl,0)
        choicesSizer.Add(triggEvtLbl,0)
        choicesSizer.Add(windOpenCtrl,0,wx.EXPAND)
        choicesSizer.Add(workModeCtrl,0,wx.EXPAND)
        choicesSizer.Add(triggEvtCtrl,0,wx.EXPAND)
        sizer.Add(choicesSizer,0,wx.ALL, 5)
        sizer.Add(filename_label, 0, wx.LEFT, 5)
        sizer.Add(recordCtrl,0,wx.EXPAND|wx.LEFT|wx.RIGHT, 5)
        fillGrid(True)
        self.Bind(wx.EVT_CLOSE, self.onClose)
        self.SetSizer(sizer)
        sizer.Layout()
        hgh += grid.GetSize()[1]
        hgh += bttnSizer.GetSize()[1]
        hgh += typeSizer.GetSize()[1]
        hgh += source_label.GetSize()[1]
        hgh += sourceCtrl.GetSize()[1]
        hgh += favorite_label.GetSize()[1]
        hgh += choicesSizer.GetSize()[1]
        hgh += filename_label.GetSize()[1]
        hgh += recordCtrl.GetSize()[1]
        self.SetClientSize(wx.Size(wDynamic[0] + 10, hgh))
        if ConfigData.pos:
            self.SetPosition(ConfigData.pos)
        else:
            self.Center()
        self.Show(True)


    def EnableAll(self, flag):
        if isinstance(flag, wx.CommandEvent):
            schedule = self.tmpData[self.lastRow][1]
            flag = 1
        for ix in range(len(self.tmpData)):
            self.tmpData[ix][0] = flag
            if self.grid.GetItem(ix, 1).GetText() == self.tmpData[ix][1]:
                if flag:
                    self.grid.CheckItem(ix)
                elif self.grid.IsChecked(ix):
                    self.grid.ToggleItem(ix)
        self.applyBttn.Enable(self.tmpData != self.plugin.data)


    def DisableAll(self, evt):
        self.EnableAll(0)


    def EnableSchedule(self, schedule, flag):
        tmpList = [item[1] for item in self.tmpData]
        if schedule in tmpList:
            ix = tmpList.index(schedule)
            self.tmpData[ix][0] = flag
            if self.grid.GetItem(ix, 1).GetText() == self.tmpData[ix][1]:
                if flag:
                    self.grid.CheckItem(ix)
                elif self.grid.IsChecked(ix):
                    self.grid.ToggleItem(ix)


    def DeleteSchedule(self, schedule):
        tmpList = [item[1] for item in self.tmpData]
        if schedule in tmpList:
            ix = tmpList.index(schedule)
            if self.grid.GetItem(ix, 1).GetText() == self.tmpData[ix][1]:
                self.grid.DeleteItem(ix)
                self.grid.Refresh()
            self.tmpData.pop(ix)


    def AddSchedule(self, schedule):
        tmpList = [item[1] for item in self.tmpData]
        if schedule[1] in tmpList:
            ix = tmpList.index(schedule[1])
            self.tmpData[ix] = schedule
            if not self.grid.GetItem(ix, 1).GetText() == self.tmpData[ix][1]:
                return
        else:
            ix = len(self.tmpData)
            self.tmpData.append(schedule)
            self.grid.InsertStringItem(ix, "")
        if schedule[0]:
            self.grid.CheckItem(ix)
        elif self.grid.IsChecked(ix):
            self.grid.ToggleItem(ix)
        self.grid.SetStringItem(ix, 1, schedule[1])
        next = self.plugin.NextRun(schedule[2], schedule[3])
        self.grid.SetStringItem(ix, 3, next)
        if self.lastRow == ix:
            evt = eg.ValueChangedEvent(ix)
            wx.PostEvent(self, evt)


    def RefreshGrid(self, ix, last, next):
        if self.grid.GetItem(ix, 1).GetText() == self.tmpData[ix][1]:
            self.grid.SetStringItem(ix, 2, last)
            self.grid.SetStringItem(ix, 3, next)


    def onClose(self, evt):
        hwnd = self.GetHandle()
        wp = GetWindowPlacement(hwnd)[4]
        #Note: GetPosition() return (-32000, -32000), if window is minimized !!!
        cdr = wx.GetClientDisplayRect()
        pos = (wp[0] + cdr[0], wp[1] + cdr[1])
        if pos != ConfigData.pos:
            ConfigData.pos = pos
            #if not eg.document.IsDirty():
            #    wx.CallAfter(eg.Notify, "DocumentChange", True)
        self.plugin.dialog = None
        wx.CallAfter(self.Show, False)
        wx.CallAfter(self.Destroy)
        evt.Skip()
#===============================================================================

def HandleRS():

    FindRS = eg.WindowMatcher(
                u'RadioSure.exe',
                None,
                u'#32770',
                None,
                None,
                None,
                True,
                0.0,
                0
            )
    hwnds = FindRS()
    res = []
    for hwnd in hwnds:
        try: #maybe already closed !!!
            curhw = GetWindow(hwnd, GW_CHILD)
            while curhw > 0:
                if GetDlgCtrlID(curhw) == 1016 and GetClassName(curhw) == 'SysListView32':
                    res.append(hwnd)
                    break
                curhw = GetWindow(curhw, GW_HWNDNEXT)
        except:
            pass
    return res
#===============================================================================

class ObservationThread:

    def __init__(self, period, evtName, evtName2, plugin):
        self.alive = False
        self.period = period
        self.evtName = evtName
        self.evtName2 = evtName2
        self.task = None
        self.oldData = ""
        self.oldData2 = ""
        self.plugin = plugin


    def RS_ObservationThread(self):
        hwnd = HandleRS()
        if hwnd:
            if self.evtName:
                data = GetWindowText(hwnd[0]).decode(eg.systemEncoding)
                if data != self.oldData and data != "Radio? Sure!":
                    self.oldData = data
                    eg.TriggerEvent(self.evtName, payload = data, prefix = "RadioSure")
            if self.evtName2:
                data = self.plugin.GetLastPlayed()
                if data and data[0] != self.oldData2:
                    self.oldData2 = data[0]
                    eg.TriggerEvent(self.evtName2, payload = data, prefix = "RadioSure")
        if self.alive:
            self.task = eg.scheduler.AddTask(self.period, self.RS_ObservationThread)


    def start(self):
        self.alive = True
        self.RS_ObservationThread()


    def isAlive(self):
        return self.alive


    def AbortObservation(self):
        eg.scheduler.CancelTask(self.task)
        self.alive = False
        self.task = None

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

def GetCtrlByID(id):

    res = None
    hwnds = HandleRS()
    if hwnds:
        try:
            res = GetDlgItem(hwnds[0], id)
        except:
            pass
    return res
#===============================================================================

def getPathFromReg():

    try:
        rs_reg = OpenKey(
            HKEY_CURRENT_USER,
            "Software\\RadioSure"
        )
        res = unicode(EnumValue(rs_reg,0)[1])
        CloseKey(rs_reg)
    except:
        res = None
    return res
#===============================================================================

def FindMonthDay(year, month, weekday, index):
    """weekday = what day of the week looking for (numbered 0-6, 0 = monday)
    index = how many occurrence of looking for (numbered 0-4 and 5 for the last day)
    Returns the day of the month (date) or 0 (if no such date exists)"""
    first_wd, length = monthrange(year, month)
    day = 1 + weekday - first_wd
    if day < 1:
        day += 7
    if index == 5:
        index = 4 if day <= length % 7 else 3
    day += 7 * index
    if day > length:
        day = 0
    return day
#===============================================================================

def getStations(nodelist):
    tmp = []
    for item in nodelist.childNodes:
        if item.nodeName[:5] == "Item-":
            title = item.getElementsByTagName('Title')[0].firstChild
            if title:
                title = title.data
            source = item.getElementsByTagName('Source')[0].firstChild
            if source:
                source = source.data
            genre  = item.getElementsByTagName('Genre')[0].firstChild
            if genre:
                genre = genre.data
            language = item.getElementsByTagName('Language')[0].firstChild
            if language:
                language = language.data
            country = item.getElementsByTagName('Country')[0].firstChild
            if country:
                country = country.data
            tmp.append([source, title, genre, language, country])
    return tmp
#===============================================================================

class MenuGrid(gridlib.Grid):

    def __init__(self, parent, lngth):
        gridlib.Grid.__init__(self, parent)
        self.SetRowLabelSize(0)
        self.SetColLabelSize(0)
        self.SetDefaultRowSize(16)
        self.SetScrollLineX(1)
        self.SetScrollLineY(1)
        self.EnableEditing(False)
        self.EnableDragColSize(False)
        self.EnableDragRowSize(False)
        self.EnableDragGridSize(False)
        self.EnableGridLines(False)
        self.SetColMinimalAcceptableWidth(8)
        self.CreateGrid(lngth, 3)
        attr = gridlib.GridCellAttr()
        attr.SetAlignment(wx.ALIGN_LEFT, wx.ALIGN_CENTRE)
        self.SetColAttr(1,attr)
        self.SetSelectionMode(gridlib.Grid.wxGridSelectRows)
        self.Bind(gridlib.EVT_GRID_CMD_SELECT_CELL, self.onGridSelectCell, self)


    def SetBackgroundColour(self, colour):
        self.SetDefaultCellBackgroundColour(colour)


    def SetForegroundColour(self, colour):
        self.SetDefaultCellTextColour(colour)


    def SetFont(self, font):
        self.SetDefaultCellFont(font)


    def GetSelection(self):
        return self.GetSelectedRows()[0]


    def Set(self, choices):
        oldLen = self.GetNumberRows()
        newLen = len(choices)
        h = self.GetDefaultRowSize()
        if oldLen > newLen:
            self.DeleteRows(0, oldLen-newLen, False)
        elif oldLen < newLen:
            self.AppendRows(newLen-oldLen, False)
        for i in range(len(choices)):
            chr = u"\u25a0" if choices[i][2] else ""
            self.SetCellValue(i,0,chr)
            self.SetCellValue(i,1," "+choices[i][0])
            chr = u"\u25ba" if choices[i][3] == -1 else ""
            self.SetCellValue(i,2, chr)
            self.SetRowSize(i,h)


    def onGridSelectCell(self, event):
        row = event.GetRow()
        self.SelectRow(row)
        if not self.IsVisible(row,1):
            self.MakeCellVisible(row,1)
        event.Skip()


    def MoveCursor(self, step):
        max = self.GetNumberRows()
        sel = self.GetSelectedRows()[0]
        new = sel + step
        if new < 0:
            new += max
        elif new > max-1:
            new -= max
        self.SetGridCursor(new, 1)
        self.SelectRow(new)
#===============================================================================

class MyTextDropTarget(EventDropTarget):

    def __init__(self, object):
        EventDropTarget.__init__(self, object)
        self.object = object


    def OnDragOver(self, x, y, dragResult):
        return wx.DragMove


    def OnData(self, dummyX, dummyY, dragResult):
        if self.GetData() and self.customData.GetDataSize() > 0:
            txt = self.customData.GetData()
            ix, evtList = self.object.GetEvtList()
            flag = True
            for lst in evtList:
                if txt in lst:
                    flag = False
                    break
            if flag:
                self.object.InsertImageStringItem(len(evtList[ix]), txt, 0)
                self.object.UpdateEvtList(ix, txt)
            else:
                PlaySound('SystemExclamation', SND_ASYNC)


    def OnLeave(self):
        pass
#===============================================================================

class EventListCtrl(wx.ListCtrl):

    def __init__(self, parent, id, evtList, ix, plugin):
        width = 205
        wx.ListCtrl.__init__(self, parent, id, style=wx.LC_REPORT |
            wx.LC_NO_HEADER | wx.LC_SINGLE_SEL, size = (width, -1))
        self.parent = parent
        self.id = id
        self.evtList = evtList
        self.ix = ix
        self.plugin = plugin
        self.sel = -1
        self.il = wx.ImageList(16, 16)
        self.il.Add(wx.BitmapFromImage(wx.Image(join(IMAGES_DIR, "event.png"), wx.BITMAP_TYPE_PNG)))
        self.SetImageList(self.il, wx.IMAGE_LIST_SMALL)
        self.InsertColumn(0, '')
        self.SetColumnWidth(0, width - 5 - SYS_VSCROLL_X)
        self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnSelect)
        self.Bind(wx.EVT_SET_FOCUS, self.OnChange)
        self.Bind(wx.EVT_LIST_INSERT_ITEM, self.OnChange)
        self.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.OnChange)
        self.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self.OnRightClick)
        self.SetToolTipString(self.plugin.text.toolTip)


    def OnSelect(self, event):
        self.sel = event.GetIndex()
        evt = eg.ValueChangedEvent(self.id, value = self)
        wx.PostEvent(self, evt)
        event.Skip()


    def OnChange(self, event):
        evt = eg.ValueChangedEvent(self.id, value = self)
        wx.PostEvent(self, evt)
        event.Skip()


    def OnRightClick(self, event):
        if not hasattr(self, "popupID1"):
            self.popupID1 = wx.NewId()
            self.popupID2 = wx.NewId()
            self.Bind(wx.EVT_MENU, self.OnDeleteButton, id=self.popupID1)
            self.Bind(wx.EVT_MENU, self.OnDeleteAllButton, id=self.popupID2)
        # make a menu
        menu = wx.Menu()
        # add some items
        menu.Append(self.popupID1, self.plugin.text.popup[0])
        menu.Append(self.popupID2, self.plugin.text.popup[1])
        # Popup the menu.  If an item is selected then its handler
        # will be called before PopupMenu returns.
        self.PopupMenu(menu)
        menu.Destroy()


    def OnDeleteButton(self, event=None):
        self.DeleteItem(self.sel)
        self.evtList[self.ix].pop(self.sel)
        evt = eg.ValueChangedEvent(self.id, value = self)
        wx.PostEvent(self, evt)
        if event:
            event.Skip()


    def OnDeleteAllButton(self, event=None):
        self.DeleteAllItems()
        evt = eg.ValueChangedEvent(self.id, value = self)
        wx.PostEvent(self, evt)
        self.evtList[self.ix] = []
        if event:
            event.Skip()


    def GetEvtList(self):
        return self.ix, self.evtList


    def UpdateEvtList(self, ix, txt):
        self.evtList[ix].append(txt)


    def SetItems(self, evtList):
        for i in range(len(evtList)):
            self.InsertImageStringItem(i, evtList[i], 0)
#===============================================================================

class MenuEventsDialog(wx.MiniFrame):

    def __init__(self, parent, plugin):
        wx.MiniFrame.__init__(
            self,
            parent,
            -1,
            style=wx.CAPTION,
            name="Menu events dialog"
        )
        self.panel = parent
        self.plugin = plugin
        self.evtList = cpy(self.panel.evtList)
        self.SetBackgroundColour(wx.NullColour)
        self.ctrl = None
        self.sel = -1


    def ShowMenuEventsDialog(self, title, labels):
        self.panel.Enable(False)
        self.panel.dialog.buttonRow.cancelButton.Enable(False)
        self.panel.EnableButtons(False)
        self.SetTitle(title)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.SetMinSize((450, 308))
        topSizer=wx.GridBagSizer(2, 20)
        textLbl_0=wx.StaticText(self, -1, labels[0])
        id = wx.NewId()
        eventsCtrl_0 = EventListCtrl(self, id, self.evtList, 0, self.plugin)
        eventsCtrl_0.SetItems(self.evtList[0])
        dt0 = MyTextDropTarget(eventsCtrl_0)
        eventsCtrl_0.SetDropTarget(dt0)
        textLbl_1=wx.StaticText(self, -1, labels[1])
        id = wx.NewId()
        eventsCtrl_1 = EventListCtrl(self, id, self.evtList, 1, self.plugin)
        eventsCtrl_1.SetItems(self.evtList[1])
        dt1 = MyTextDropTarget(eventsCtrl_1)
        eventsCtrl_1.SetDropTarget(dt1)
        textLbl_2=wx.StaticText(self, -1, labels[2])
        id = wx.NewId()
        eventsCtrl_2 = EventListCtrl(self, id, self.evtList, 2, self.plugin)
        eventsCtrl_2.SetItems(self.evtList[2])
        dt2 = MyTextDropTarget(eventsCtrl_2)
        eventsCtrl_2.SetDropTarget(dt2)
        textLbl_3=wx.StaticText(self, -1, labels[3])
        id = wx.NewId()
        eventsCtrl_3 = EventListCtrl(self, id, self.evtList, 3, self.plugin)
        eventsCtrl_3.SetItems(self.evtList[3])
        dt3 = MyTextDropTarget(eventsCtrl_3)
        eventsCtrl_3.SetDropTarget(dt3)
        textLbl_4=wx.StaticText(self, -1, labels[4])
        id = wx.NewId()
        eventsCtrl_4 = EventListCtrl(self, id, self.evtList, 4, self.plugin)
        eventsCtrl_4.SetItems(self.evtList[4])
        dt4 = MyTextDropTarget(eventsCtrl_4)
        eventsCtrl_4.SetDropTarget(dt4)
        deleteSizer = wx.BoxSizer(wx.VERTICAL)
        delOneBtn = wx.Button(self, -1, self.plugin.text.popup[0])
        delBoxBtn = wx.Button(self, -1, self.plugin.text.popup[1])
        clearBtn  = wx.Button(self, -1, self.plugin.text.clear)
        deleteSizer.Add(delOneBtn, 1, wx.EXPAND)
        deleteSizer.Add(delBoxBtn, 1, wx.EXPAND|wx.TOP,5)
        deleteSizer.Add(clearBtn, 1, wx.EXPAND|wx.TOP,5)

        topSizer.Add(textLbl_0, (0,0))
        topSizer.Add(eventsCtrl_0, (1,0), flag = wx.EXPAND)
        topSizer.Add(textLbl_1, (0,1))
        topSizer.Add(eventsCtrl_1, (1,1), flag = wx.EXPAND)
        topSizer.Add(textLbl_2, (2,0),flag = wx.TOP, border = 8)
        topSizer.Add(eventsCtrl_2, (3,0), flag = wx.EXPAND)
        topSizer.Add(textLbl_3, (2,1), flag = wx.TOP, border = 8)
        topSizer.Add(eventsCtrl_3, (3,1), flag = wx.EXPAND)
        topSizer.Add(textLbl_4, (4,0), flag = wx.TOP, border = 8)
        topSizer.Add(eventsCtrl_4, (5,0), flag = wx.EXPAND)
        topSizer.Add(deleteSizer, (5,1), flag = wx.EXPAND)

        line = wx.StaticLine(self, -1, size=(20,-1),pos = (200,0), style=wx.LI_HORIZONTAL)
        btn1 = wx.Button(self, wx.ID_OK)
        btn1.SetLabel(self.plugin.text.ok)
        btn1.SetDefault()
        btn2 = wx.Button(self, wx.ID_CANCEL)
        btn2.SetLabel(self.plugin.text.cancel)
        btnsizer = wx.StdDialogButtonSizer()
        btnsizer.AddButton(btn1)
        btnsizer.AddButton(btn2)
        btnsizer.Realize()
        sizer.Add(topSizer,0,wx.ALL,10)
        sizer.Add(line, 0, wx.EXPAND|wx.ALIGN_CENTER|wx.TOP|wx.BOTTOM,5)
        sizer.Add(btnsizer, 0, wx.EXPAND|wx.RIGHT, 10)
        sizer.Add((1,6))
        self.SetSizer(sizer)
        sizer.Fit(self)


        def onFocus(evt):
            ctrl = evt.GetValue()
            if ctrl != self.ctrl:
                if self.ctrl:
                    self.ctrl.SetItemState(-1, wx.LIST_MASK_STATE, wx.LIST_STATE_SELECTED)
                self.ctrl = ctrl
            sel = self.ctrl.sel
            if sel != -1:
                self.sel = sel
            flag = self.ctrl.GetSelectedItemCount() > 0
            delOneBtn.Enable(flag)
            delBoxBtn.Enable(flag)
            evt.Skip()
        eventsCtrl_0.Bind(eg.EVT_VALUE_CHANGED, onFocus)
        eventsCtrl_1.Bind(eg.EVT_VALUE_CHANGED, onFocus)
        eventsCtrl_2.Bind(eg.EVT_VALUE_CHANGED, onFocus)
        eventsCtrl_3.Bind(eg.EVT_VALUE_CHANGED, onFocus)
        eventsCtrl_4.Bind(eg.EVT_VALUE_CHANGED, onFocus)


        def onDelOneBtn(evt):
            self.ctrl.OnDeleteButton()
            delOneBtn.Enable(False)
            delBoxBtn.Enable(False)
            evt.Skip()
        delOneBtn.Bind(wx.EVT_BUTTON, onDelOneBtn)


        def onDelBoxBtn(evt):
            self.ctrl.OnDeleteAllButton()
            delOneBtn.Enable(False)
            delBoxBtn.Enable(False)
            evt.Skip()
        delBoxBtn.Bind(wx.EVT_BUTTON, onDelBoxBtn)


        def onClearBtn(evt):
            eventsCtrl_0.DeleteAllItems()
            eventsCtrl_1.DeleteAllItems()
            eventsCtrl_2.DeleteAllItems()
            eventsCtrl_3.DeleteAllItems()
            eventsCtrl_4.DeleteAllItems()
            delOneBtn.Enable(False)
            delBoxBtn.Enable(False)
            self.evtList = [[],[],[],[],[]]
            evt.Skip()
        clearBtn.Bind(wx.EVT_BUTTON, onClearBtn)


        def onClose(evt):
            self.panel.Enable(True)
            self.panel.dialog.buttonRow.cancelButton.Enable(True)
            self.panel.EnableButtons(True)
            self.GetParent().GetParent().Raise()
            self.Destroy()
            self.panel.setFocus()
        self.Bind(wx.EVT_CLOSE, onClose)


        def onCancel(evt):
            self.panel.Enable(True)
            self.panel.dialog.buttonRow.cancelButton.Enable(True)
            self.panel.EnableButtons(True)
            self.Close()
        btn2.Bind(wx.EVT_BUTTON,onCancel)


        def onOK(evt):
            self.panel.evtList = self.evtList
            self.Close()
        btn1.Bind(wx.EVT_BUTTON,onOK)

        sizer.Layout()
        self.Raise()
        self.Show()
#===============================================================================

class Menu(wx.Frame):

    def __init__(
        self,
        fore,
        back,
        foreSel,
        backSel,
        fontInfo,
        flag,
        plugin,
        event,
        monitor,
        hWnd,
        evtList,
        ix,
        ):
        wx.Frame.__init__(
            self,
            None,
            -1,
            'MPC_menu',
            style = wx.STAY_ON_TOP|wx.SIMPLE_BORDER
        )
        self.flag = False
        self.monitor = 0
        self.oldMenu = []

        self.fore     = fore
        self.back     = back
        self.foreSel  = foreSel
        self.backSel  = backSel
        self.fontInfo = fontInfo
        self.flag     = flag
        self.plugin   = plugin
        self.monitor  = monitor
        self.hWnd     = hWnd
        self.evtList  = evtList
        eg.TriggerEvent("OnScreenMenu.%s" % self.plugin.text.opened, prefix = "RadioSure")
        for evt in self.evtList[0]:
            eg.Bind(evt, self.onUp)
        for evt in self.evtList[1]:
            eg.Bind(evt, self.onDown)
        for evt in self.evtList[2]:
            eg.Bind(evt, self.onLeft)
        for evt in self.evtList[3]:
            eg.Bind(evt, self.onRight)
        for evt in self.evtList[4]:
            eg.Bind(evt, self.onEscape)
        self.menuHwnd, self.menu = self.plugin.GetRS_Menu(self.hWnd)
        self.items = self.plugin.GetItemList(self.menuHwnd, self.menu)
        self.choices = [item[0] for item in self.items]
        self.menuGridCtrl = MenuGrid(self, len(self.choices))
        mainSizer = wx.BoxSizer(wx.VERTICAL)
        self.SetSizer(mainSizer)
        mainSizer.Add(self.menuGridCtrl, 0, wx.EXPAND)
        self.Bind(wx.EVT_CLOSE, self.onClose)
        self.Bind(gridlib.EVT_GRID_CMD_CELL_LEFT_DCLICK, self.onDoubleClick, self.menuGridCtrl)
        self.Bind(wx.EVT_CHAR_HOOK, self.onFrameCharHook)
        font = wx.FontFromNativeInfoString(fontInfo)
        self.menuGridCtrl.SetFont(font)
        arial = wx.FontFromNativeInfoString(ARIAL_INFO)
        self.SetFont(font)
        hght = self.GetTextExtent('X')[1]
        for n in range(1,1000):
            arial.SetPointSize(n)
            self.SetFont(arial)
            h = self.GetTextExtent(u"\u25a0")[1]
            if h > hght:
                break
        arial.SetPointSize(2*n/3)
        self.SetFont(arial)
        self.w0 = 2 * self.GetTextExtent(u"\u25a0")[0]
        attr = gridlib.GridCellAttr()
        attr.SetFont(arial)
        attr.SetAlignment(wx.ALIGN_CENTRE, wx.ALIGN_CENTRE)
        self.menuGridCtrl.SetColAttr(0,attr)
        for n in range(1,1000):
            arial.SetPointSize(n)
            self.SetFont(arial)
            h = self.GetTextExtent(u"\u25ba")[1]
            if h > hght:
                break
        arial.SetPointSize(n/2)
        self.SetFont(arial)
        self.w2 = 2 * self.GetTextExtent(u"\u25ba")[0]
        attr = gridlib.GridCellAttr()
        attr.SetFont(arial)
        attr.SetAlignment(wx.ALIGN_RIGHT, wx.ALIGN_CENTRE)
        self.menuGridCtrl.SetColAttr(2,attr)
        self.SetFont(font)
        self.SetBackgroundColour((0, 0, 0))
        self.menuGridCtrl.SetBackgroundColour(self.back)
        self.menuGridCtrl.SetForegroundColour(self.fore)
        self.menuGridCtrl.SetSelectionBackground(self.backSel)
        self.menuGridCtrl.SetSelectionForeground(self.foreSel)
        if self.flag:
            self.timer=MyTimer(t = 5.0, plugin = self.plugin)
        self.menuGridCtrl.Set(self.items)
        self.UpdateMenu(ix == 0, ix)
        self.plugin.menuDlg = self
        wx.Yield()
        SetEvent(event)


    def DrawMenu(self, ix):
        self.Show(False)
        self.menuGridCtrl.SetGridCursor(ix, 1)
        self.menuGridCtrl.SelectRow(ix)
        monDim = GetMonitorDimensions()
        try:
            x,y,ws,hs = monDim[self.monitor]
        except IndexError:
            x,y,ws,hs = monDim[0]
        # menu height calculation:
        h=self.GetCharHeight()+4
        for i in range(len(self.choices)):
            self.menuGridCtrl.SetRowSize(i,h)
            self.menuGridCtrl.SetCellValue(i,1," "+self.choices[i])
            if self.items[i][3] == -1:
                self.menuGridCtrl.SetCellValue(i,2, u"\u25ba")
        height0 = len(self.choices)*h
        height1 = h*((hs-20)/h)
        height = min(height0, height1)+6
        # menu width calculation:
        width_lst=[]
        for item in self.choices:
            width_lst.append(self.GetTextExtent(item+' ')[0])
        width = max(width_lst)+8
        self.menuGridCtrl.SetColSize(0,self.w0)
        self.menuGridCtrl.SetColSize(1,width)
        self.menuGridCtrl.SetColSize(2,self.w2)
        self.menuGridCtrl.ForceRefresh()
        width = width + self.w0 + self.w2
        if height1 < height0:
            width += SYS_VSCROLL_X
        if width > ws-50:
            if height + SYS_HSCROLL_Y < hs:
                height += SYS_HSCROLL_Y
            width = ws-50
        width += 6
        x_pos = x + (ws - width)/2
        y_pos = y + (hs - height)/2
        self.SetDimensions(x_pos,y_pos,width,height)
        self.menuGridCtrl.SetDimensions(2, 2, width-6, height-6, wx.SIZE_AUTO)
        self.Show(True)
        self.Raise()


    def UpdateMenu(self, root = False, ix = 0):
        if root:
            self.menuHwnd, self.menu = self.plugin.GetRS_Menu(self.hWnd)
        else:
            self.menuHwnd, self.menu = self.GetSubMenuExt(self.hWnd, ix)
            ix = 0
        self.items = self.plugin.GetItemList(self.menuHwnd, self.menu)
        if len(self.items)==0:
            PlaySound('SystemExclamation', SND_ASYNC)
            eg.PrintError("Please report: %i, %i, %i, %i" % (ix, int(root), self.menuHwnd, self.menu))
            #self.menu,ix = self.oldMenu.pop()
            #self.items = self.plugin.GetItemList(self.hWnd, self.menu)
        self.choices = [item[0] for item in self.items]
        self.menuGridCtrl.Set(self.items)
        self.DrawMenu(ix)


    def MoveCursor(self, step):
        max=len(self.choices)
        if max > 0:
            self.menuGridCtrl.MoveCursor(step)


    def onUp(self, event):
        wx.CallAfter(self.menuGridCtrl.MoveCursor, -1)
        return True #stop processing this event !!!


    def onDown(self, event):
        wx.CallAfter(self.menuGridCtrl.MoveCursor, 1)
        return True #stop processing this event !!!


    def onLeft(self, event):
        if len(self.oldMenu) > 0:
            ix = self.oldMenu.pop()
            wx.CallAfter(self.UpdateMenu, True, ix)
        else:
            wx.CallAfter(self.destroyMenu)
        return True #stop processing this event !!!


    def onRight(self, event):
        wx.CallAfter(self.DefaultAction)
        return True #stop processing this event !!!


    def onEscape(self, event):
        wx.CallAfter(self.destroyMenu)
        return True #stop processing this event !!!


    def GetSubMenuExt(self, hWnd, ix):
        menu, hMenu = self.plugin.GetRS_Menu(hWnd)
        if menu:
            hMenu = GetSubMenu(hMenu, ix)
            return (menu, hMenu)


    def DefaultAction(self):
        sel = self.menuGridCtrl.GetSelection()
        item = self.items[sel]
        id = item[3]
        if id != -1:
            self.destroyMenu()
            SendMessage(self.hWnd, WM_COMMAND, id, 0)
        else:
            self.oldMenu.append(sel)
            wx.CallAfter(self.UpdateMenu, False, item[1])


    def onFrameCharHook(self, event):
        keyCode = event.GetKeyCode()
        if keyCode == wx.WXK_F4:
            if event.AltDown():
                self.destroyMenu()
        elif keyCode == wx.WXK_RETURN or keyCode == wx.WXK_NUMPAD_ENTER:
            self.DefaultAction()
        elif keyCode == wx.WXK_RIGHT or keyCode == wx.WXK_NUMPAD_RIGHT:
            self.DefaultAction()
        elif keyCode == wx.WXK_ESCAPE:
            self.destroyMenu()
        elif keyCode == wx.WXK_UP or keyCode == wx.WXK_NUMPAD_UP:
            self.menuGridCtrl.MoveCursor(-1)
        elif keyCode == wx.WXK_DOWN or keyCode == wx.WXK_NUMPAD_DOWN:
            self.menuGridCtrl.MoveCursor(1)
        elif keyCode == wx.WXK_LEFT or keyCode == wx.WXK_NUMPAD_LEFT:
            if len(self.oldMenu) > 0:
                ix = self.oldMenu.pop()
                wx.CallAfter(self.UpdateMenu, True, ix)
            else:
                self.destroyMenu()
        else:
            event.Skip()


    def onDoubleClick(self, event):
        self.DefaultAction()
        event.Skip()


    def onClose(self, event):
        self.Show(False)
        self.Destroy()
        self.plugin.menuDlg = None


    def destroyMenu(self, event = None):
        for evt in self.evtList[0]:
            eg.Unbind(evt, self.onUp)
        for evt in self.evtList[1]:
            eg.Unbind(evt, self.onDown)
        for evt in self.evtList[2]:
            eg.Unbind(evt, self.onLeft)
        for evt in self.evtList[3]:
            eg.Unbind(evt, self.onRight)
        for evt in self.evtList[4]:
            eg.Unbind(evt, self.onEscape)
        if self.flag:
            self.timer.Cancel()
        eg.TriggerEvent("OnScreenMenu.%s" % self.plugin.text.closed, prefix = "RadioSure")
        self.Close()
#===============================================================================


class RadioSure(eg.PluginBase):

    text=Text
    menuDlg = None
    RadioSurePath = u''
    xmlPath = u''
    data = []
    tmpData = []
    dialog = None
    manager = None
    Favorites = []
    History = []
    Current = ['','']
    FavIx = -1
    HistIx = -1
    List = None
    maxFav = None
    submenus = None


    def GetRS_Menu(self, hwnd):

        WM_CONTEXTMENU   = 0x007B
        OBJID_CLIENT     = 0xFFFFFFFC

        class RECT(Structure):
            _fields_ = [
                ('left', c_long),
                ('top', c_long),
                ('right', c_long),
                ('bottom', c_long),
            ]

        class MENUBARINFO(Structure):
            _fields_ = [
                ('cbSize',  c_ulong),
                ('rcBar',  RECT),            # rect of bar, popup, item
                ('hMenu',  c_long),          # real menu handle of bar, popup
                ('hwndMenu',  c_long),       # hwnd of item submenu if one
                ('fBarFocused',  c_int, 1),  # bar, popup has the focus
                ('fFocused',  c_int, 1),     # item has the focus
            ]

        findMenu = eg.WindowMatcher(
                    u'RadioSure.exe',
                    None,
                    u'#32768',
                    None,
                    None,
                    None,
                    True,
                    0.0,
                    0
                )
        PostMessage(hwnd, WM_CONTEXTMENU, hwnd, 0x00010001)
        menu = []
        i = 0
        while len(menu) == 0:
            menu = findMenu()
            i+=1
            if i > 1000:
                break
        if menu:
            menu = menu[0]
            mbi = MENUBARINFO()
            mbi.cbSize = sizeof(mbi)
            if _user32.GetMenuBarInfo(
                menu,
                OBJID_CLIENT,
                0,
                byref(mbi)
            ):
                return (menu, mbi.hMenu)
        return (None, None)


    def GetItemList(self, hWnd, hMenu):
        WM_INITMENUPOPUP = 0x0117
        MF_BYPOSITION    = 1024
        MF_GRAYED        = 1
        MF_DISABLED      = 2
        MF_CHECKED       = 8
        MF_SEPARATOR     = 2048
        SendMessage(hWnd, WM_INITMENUPOPUP, hMenu, 0) #REFRESH MENU STATE !!!
        itemList = []
        itemName = c_buffer("\000" * 128)
        count = GetMenuItemCount(hMenu)
        for i in range(count):
            _user32.GetMenuStringA(c_int(hMenu),
                                         c_int(i),
                                         itemName,
                                         c_int(len(itemName)),
                                         MF_BYPOSITION)
            hMenuState = _user32.GetMenuState(c_int(hMenu),
                                                   c_int(i),
                                                   MF_BYPOSITION)
            id = _user32.GetMenuItemID(c_int(hMenu), c_int(i))
    #        if hMenuState & (MF_GRAYED|MF_DISABLED|MF_SEPARATOR):
            if hMenuState & (MF_GRAYED|MF_DISABLED):
                continue
            item = itemName.value.replace("&","").split("\t")[0]
            if item == "" and id == 0:
                continue
            checked = bool(hMenuState & MF_CHECKED)
            itemList.append((item, i, checked, id))
        PostMessage(hWnd, WM_CLOSE, 0, 0)
        return itemList


    def GetLanguageXml(self):
        xmltoparse = u'%s\\RadioSure.xml' % self.xmlpath
        xmltoparse = xmltoparse.encode(FSE) if isinstance(xmltoparse, unicode) else xmltoparse
        xmldoc = miniDom.parse(xmltoparse)
        general = xmldoc.getElementsByTagName('General')
        if general: #NOTE: don't use general[0].getElementsByTagName('Language') !!!!!!!!!!!!!!
            langNodes = [node for node in general[0].childNodes if node.localName =="Language"]
            if langNodes:
                langFile = abspath(join(self.RadioSurePath+"\\Lang", langNodes[0].firstChild.data))
                langFile = langFile.encode(FSE) if isinstance(langFile, unicode) else langFile
                languageXml = miniDom.parse(langFile)
                return languageXml


    def GetLastPlayed(self):
        xmltoparse = u'%s\\RadioSure.xml' % self.xmlpath
        xmltoparse = xmltoparse.encode(FSE) if isinstance(xmltoparse, unicode) else xmltoparse
        xmldoc = miniDom.parse(xmltoparse)
        lastPlayed = xmldoc.getElementsByTagName('LastPlayed')
        if lastPlayed:
            try:
                source = lastPlayed[0].getElementsByTagName('Source')[0].firstChild
                source = source.data if source else ""
                title = lastPlayed[0].getElementsByTagName('Title')[0].firstChild
                title = title.data  if title else ""
                genre = lastPlayed[0].getElementsByTagName('Genre')[0].firstChild
                genre = genre.data if genre else ""
                language = lastPlayed[0].getElementsByTagName('Language')[0].firstChild
                language = language.data if language else ""
                country = lastPlayed[0].getElementsByTagName('Country')[0].firstChild
                country = country.data if country else ""
                return (source, title, genre, language, country)
            except:
                pass
        return None


    def GetOneInstance(self):
        xmltoparse = u'%s\\RadioSure.xml' % self.xmlpath
        xmltoparse = xmltoparse.encode(FSE) if isinstance(xmltoparse, unicode) else xmltoparse
        xmldoc = miniDom.parse(xmltoparse)
        advanced = xmldoc.getElementsByTagName('Advanced')
        if advanced:
            oneInstance = advanced[0].getElementsByTagName('One_instance')[0].firstChild.data
            return oneInstance


    def GetStrings(self):
        language = self.GetLanguageXml()
        if language:
            res = {}
            mainWindow = language.getElementsByTagName('MainWindow')
            res['stop'] = mainWindow[0].getElementsByTagName('Stop')[0].firstChild.data
            res['unmute'] = mainWindow[0].getElementsByTagName('Unmute')[0].firstChild.data
            res['stopRec'] = mainWindow[0].getElementsByTagName('StopRecording')[0].firstChild.data
            return res


    def GetSubmenuStrings(self):
        choices = [self.text.root]
        language = self.GetLanguageXml()
        if language:
            mainWindow = language.getElementsByTagName('MainWindow')
            equaliser = language.getElementsByTagName('EQUALIZER')
            sleeptimer = language.getElementsByTagName('SleepTimer')
            favorites = language.getElementsByTagName('Favorites')
#            choices.append(favorites[0].getElementsByTagName('Title')[0].firstChild.data)
            for fav in favorites:
                title = fav.getElementsByTagName('Title')
                if title:
                    break
            choices.append(title[0].firstChild.data)
            choices.append(mainWindow[0].getElementsByTagName('Back')[0].firstChild.data)
            choices.append(equaliser[0].getElementsByTagName('Title')[0].firstChild.data)
            choices.append(mainWindow[0].getElementsByTagName('WindowMenu')[0].firstChild.data)
            choices.append(mainWindow[0].getElementsByTagName('ClipboardMenu')[0].firstChild.data)
            choices.append(sleeptimer[0].getElementsByTagName('Title')[0].firstChild.data)
            choices.append(mainWindow[0].getElementsByTagName('Language')[0].firstChild.data)
        return choices


    def GetRS_Status(self, hwnd):
        menu, hMenu = self.GetRS_Menu(hwnd)
        if menu:
            menuItems = self.GetItemList(menu, hMenu)
            strings = self.GetStrings()
            if menuItems and strings:
                res = [
                    strings['stop'] == menuItems[0][0],    # Playing
                    strings['unmute'] == menuItems[1][0],  # Muted
                    strings['stopRec'] == menuItems[2][0], # Recording
                    menuItems[3][2]        # Record only current track
                ]
                return res


    def GetMenuItem(self, hwnd, indx): # indx = 7 for Fav, 8 for Hist, 9 for Equalizer
        menu, hMenu = self.GetRS_Menu(hwnd)
        if menu:
            hMenu = GetSubMenu(hMenu, indx)
            menuItems = self.GetItemList(menu, hMenu)
            flags = [item[2] for item in menuItems]
            if True in flags:
                ix = flags.index(True)
                return (ix, unicode(menuItems[ix][0].decode(eg.systemEncoding)))
        return (-1, "")


    def RefreshVariables(self):
        xmltoparse = u'%s\\RadioSure.xml' % self.xmlpath
        xmltoparse = xmltoparse.encode(FSE) if isinstance(xmltoparse, unicode) else xmltoparse
        if not exists(xmltoparse):
            return
        xmldoc = miniDom.parse(xmltoparse)
        lastPlayed = xmldoc.getElementsByTagName('LastPlayed')
        if lastPlayed:
            lastPlayed=lastPlayed[0]
            src = lastPlayed.getElementsByTagName('Source')
            if src:
                src = src[0].firstChild.data
            else:
                src = ""

            ttl = lastPlayed.getElementsByTagName('Title')
            if ttl:
                ttl = ttl[0].firstChild.data
            else:
                ttl = ""
            self.Current = [src, ttl]
        else:
            self.Current = ["", ""]
        histNode = xmldoc.getElementsByTagName('History')
        if histNode:
            self.History = getStations(histNode[0])
        else:
            self.History = []
        favNode = xmldoc.getElementsByTagName('Favorites')
        if favNode:
            self.Favorites = getStations(favNode[0])
        else:
            self.Favorites = []
        tmp = [item[:2] for item in self.Favorites]
        if self.Current in tmp:
            self.FavIx = tmp.index(self.Current)
        else:
            self.FavIx = -1
        tmp = [item[:2] for item in self.History]
        if self.Current in tmp:
            self.HistIx = tmp.index(self.Current)
        else:
            self.HistIx = -1
        return self.Favorites


    def NextRun(self, type, data):

        def FindRunDateTime(runList, cond):
            runList.sort()
            runDateTime = ""
            if len(runList) > 0:
                if not cond:
                    return runList[0]
                found = False
                for item in runList:
                    if item.weekday() > 4:
                        found = True
                        break
                    else:
                        if (item.month, item.day) in self.holidays[0]:
                            pass
                        elif (item.year, item.month, item.day) in self.holidays[1]:
                            pass
                        else:
                            found = True
                            break
                if found:
                    runDateTime = item
            return runDateTime

        now = dt.now()
        now = now.replace(microsecond = 0) + td(seconds = 2)
        runTime = dt.strptime(data[0], "%H:%M:%S").time()
        if type == 0: # once or yearly
            runDate = dt.strptime(data[2], '%Y-%m-%d')
            runDateTime = dt.combine(runDate, runTime)
            if now < runDateTime:
                return str(runDateTime)
            elif not data[3]:
                return ""
            else:
                if runDateTime.replace(year = now.year) < now:
                    return str(runDateTime.replace(year = now.year + 1))
                else:
                    return str(runDateTime.replace(year = now.year))
        elif type == 1: # daily
            runDateTime = dt.combine(now.date(), runTime)
            if now.time() > runTime:
                runDateTime += td(days = 1)
            return str(runDateTime)
        elif type == 2: # weekly
            if not data[2]:
                return ""
            runDateTime = dt.combine(now.date(), runTime)
            weekdaysLower = []
            weekdaysLarger = []
            nowDay = now.weekday()
            for weekday in range(7):
                if 2**weekday & data[2]:
                    if weekday < nowDay or (weekday == nowDay and now.time() > runTime):
                        weekdaysLower.append(weekday)
                    else:
                        weekdaysLarger.append(weekday)
            if not data[4] and not data[3]: # without holiday check
                if len(weekdaysLarger) > 0:
                    delta = weekdaysLarger[0] - nowDay
                    return str(runDateTime + td(days = delta))
                delta = 7 + weekdaysLower[0] - nowDay
                return str(runDateTime + td(days = delta))
            elif data[4]: # holiday check
                found = False
                shift = 0
                while True:
                    for day in weekdaysLarger:
                        delta = day + shift - nowDay
                        tmpRunDT = runDateTime + td(days = delta)
                        if tmpRunDT.weekday() > 4: # weekend
                            found = True
                            break
                        else: # workday
                            if (tmpRunDT.month, tmpRunDT.day) in self.holidays[0]:
                                pass
                            elif (tmpRunDT.year, tmpRunDT.month, tmpRunDT.day) in self.holidays[1]:
                                pass
                            else:
                                found = True
                                break
                    if found:
                        break
                    shift += 7
                    for day in weekdaysLower:
                        delta = day + shift - nowDay
                        tmpRunDT = runDateTime + td(days = delta)
                        if tmpRunDT.weekday() > 4: # weekend
                            found = True
                            break
                        else: # workday
                            if (tmpRunDT.month, tmpRunDT.day) in self.holidays[0]:
                                pass
                            elif (tmpRunDT.year, tmpRunDT.month, tmpRunDT.day) in self.holidays[1]:
                                pass
                            else:
                                found = True
                                break
                    if found:
                        break
                return str(tmpRunDT)
            else: # holiday_2 check
                if len(weekdaysLarger) > 0:
                    Delta = weekdaysLarger[0] - nowDay
                else:
                    Delta = 7 + weekdaysLower[0] - nowDay
                start = 0 if now.time() < runTime else 1
                found = False
                for delta in range(start, Delta):
                    tmpRunDT = runDateTime + td(days = delta)
                    if tmpRunDT.weekday() < 5:
                        if (tmpRunDT.month, tmpRunDT.day) in self.holidays[0]:
                            found = True
                            break
                        elif (tmpRunDT.year, tmpRunDT.month, tmpRunDT.day) in self.holidays[1]:
                            found = True
                            break
                return str(tmpRunDT if found else runDateTime + td(days = Delta))
        elif type == 3: # monthly/weekday
            if data[2] == 0 or data[3] == 0 or (data[4] + data[5]) == 0:
                return ""
            currMonth = now.month
            currYear = now.year
            monthsInt = data[4] + (data[5] << 6)
            months = []
            for month in range(1,13):
                if 2 ** (month - 1) & monthsInt:
                    months.append(month)
            if currMonth in months:
                runList = []
                for ix in range(6):
                    if 2 ** ix & data[2]:
                        for weekday in range(7):
                            if 2 ** weekday & data[3]:
                                day = FindMonthDay(currYear, currMonth, weekday, ix)
                                if day:
                                    runDateTime = dt.combine(dt(currYear, currMonth, day).date(), runTime)
                                    if now < runDateTime:
                                        runList.append(runDateTime)
                tmpRunDT = FindRunDateTime(runList, data[6])
                if tmpRunDT:
                    return str(tmpRunDT)
            lower = []
            larger = []
            for month in months:
                if month > currMonth:
                    larger.append(month)
                else: #month <= currMonth:
                    lower.append(month)
            year = currYear
            tmpRunDT = None
            while True:
                for month in larger:
                    runList = []
                    for ix in range(6):
                        if 2 ** ix & data[2]:
                            for weekday in range(7):
                                if 2 ** weekday & data[3]:
                                    day = FindMonthDay(year, month, weekday, ix)
                                    if day:
                                        runDateTime = dt.combine(dt(year, month, day).date(), runTime)
                                        runList.append(runDateTime)
                    tmpRunDT = FindRunDateTime(runList, data[6])
                    if tmpRunDT:
                        break
                if tmpRunDT:
                    break
                year += 1
                for month in lower:
                    runList = []
                    for ix in range(6):
                        if 2 ** ix & data[2]:
                            for weekday in range(7):
                                if 2 ** weekday & data[3]:
                                    day=FindMonthDay(year, month, weekday, ix)
                                    if day:
                                        runDateTime = dt.combine(dt(year, month, day).date(), runTime)
                                        runList.append(runDateTime)
                    tmpRunDT = FindRunDateTime(runList, data[6])
                    if tmpRunDT:
                        break
                if tmpRunDT:
                    break
            return str(tmpRunDT)
        elif type == 4: #monthly/day
            if (data[2] + data[3] + data[4] + data[5]) == 0 or (data[6] + data[7]) == 0:
                return ""
            runList = []
            currMonth = now.month
            currYear = now.year
            monthsInt = data[6] + (data[7] << 6)
            daysInt = data[2] + (data[3] << 8) + (data[4] << 16) + (data[5] << 24)
            days = []
            for day in range(1, 32):
                if 2 ** (day - 1) & daysInt:
                    days.append(day)
            months = []
            for month in range(1, 13):
                if 2 ** (month - 1) & monthsInt:
                    months.append(month)
            if currMonth in months:
                for day in days:
                    if day > monthrange(currYear, currMonth)[1]:
                        break
                    runDateTime = dt.combine(dt(currYear, currMonth, day).date(), runTime)
                    if now < runDateTime:
                        runList.append(runDateTime)
            if len(runList) == 0:
                lower = []
                larger = []
                nextMonth = None
                for month in months:
                    if month > currMonth:
                        larger.append(month)
                    else: #month<=currMonth:
                        lower.append(month)
                if len(larger) > 0:
                    nextYear = currYear
                    for month in larger:
                        for day in days:
                            if day > monthrange(nextYear, month)[1]:
                                break
                            runDateTime = dt.combine(dt(nextYear, month, day).date(), runTime)
                            runList.append(runDateTime)
                if len(runList) == 0 and len(lower) > 0:
                    nextYear = currYear + 1
                    for month in lower:
                        for day in days:
                            if day > monthrange(nextYear, month)[1]:
                                break
                            runDateTime = dt.combine(dt(nextYear, month, day).date(), runTime)
                            runList.append(runDateTime)
            if len(runList) > 0:
                return str(min(runList))
            else:
                return ""
        else: #type == 5: #periodically
            runDate = dt.strptime(data[2], '%Y-%m-%d')
            runDateTime = dt.combine(runDate, runTime)
            if now < runDateTime:
                return str(runDateTime)
            elif data[4] in (0, 1): #unit =  minute or hour
                period =  data[3] * 60 if not data[4] else data[3] * 3600
                if period < 86400 and not 86400 % period:
                    if now.time() > runTime:
                        date = now.date()
                    else:
                        date = now.date() - td(days = 1)
                    runDateTime = dt.combine(date, runTime)
                delta = now - runDateTime
                delta = delta.seconds + 86400 * delta.days
                share = delta / period
                share += 1
                delta = td(seconds = share * period)
                return str(runDateTime + delta)
            elif data[4] in (2, 3): #unit = day or week
                period = data[3] if data[4] == 2 else 7 * data[3]
                delta = (now - runDateTime).days
                share = delta / period
                if not delta % period:
                    if now.time() < runTime:
                        return str(dt.combine(now.date(), runTime))
                share += 1
                delta = td(days = share * period)
                return str(runDateTime + delta)
            elif data[4] == 4: #unit = month
                period = data[3]
                month = runDateTime.month
                year = runDateTime.year
                while now > runDateTime:
                    year += period / 12
                    m = month+period % 12
                    if m > 12:
                        year += 1
                        month = m % 12
                    else:
                        month = m
                    runDateTime = runDateTime.replace(year = year).replace(month = month)
                return str(runDateTime)
            else: # data[4] == 6: #unit = year
                period = data[3]
                year = runDateTime.year
                while now > runDateTime:
                    year += period
                    runDateTime = runDateTime.replace(year = year)
                return str(runDateTime)


    def updateLogFile(self, line, blank = False):
        if not self.logfile:
            return
        f = openFile(self.logfile, encoding='utf-8', mode='a')
        if blank:
            f.write("\r\n")
        f.write("%s  %s\r\n" % (str(dt.now())[:19], line))
        f.close()


    def Execute(self, params, immed = False):
        next = self.NextRun(params[2], params[3])
        modes = params[7]
        playRec = modes & 6
        args = []
        if playRec != 6:
            args.append(u'%s\\RadioSure.exe' % self.RadioSurePath)
            if playRec:
                args.append("/record")
            else:
                args.append("/play")
            if playRec == 4:
                args.append("/mute")
            if modes & 1:
                args.append("/hidden")
            args.append(u'/source="%s"' % params[5])
            duration = 60*int(params[3][1][:2])+int(params[3][1][-2:])
            if duration:
                args.append('/duration=%i' % duration)
            if params[6]:
                recfile = eg.ParseString(params[6])
                try:
                    recfile = eval(recfile)
                except:
                    pass
                args.append(u'/filename="%s"' % recfile)
            elif playRec:
                args.append(u'/filename="%s"' % params[1])
            Popen(args)
        if not immed and next: # new schedule, if valid next run time and not TEST/IMMEDIATELY run
            startTicks = mktime(strptime(next, "%Y-%m-%d %H:%M:%S"))
            eg.scheduler.AddTaskAbsolute(startTicks, self.RadioSureScheduleRun, params[1])
        triggEvt = modes & 24
        if triggEvt == 8:
            eg.TriggerEvent(self.text.launched, prefix = "RadioSure", payload = params[1])
        elif triggEvt == 16:
            eg.TriggerEvent(self.text.launched, prefix = "RadioSure", payload = params)
        return (next, my_list2cmdline(args) if args else None)


    def RadioSureScheduleRun(self, schedule):
        data = self.data
        ix = [item[1] for item in data].index(schedule)
        next, cmdline = self.Execute(data[ix])
        last = str(dt.now())[:19]
        self.data[ix][4] = last
        if self.dialog:
            tmpList = [item[1] for item in self.tmpData]
            if schedule in tmpList:
                ixTmp = tmpList.index(schedule)
                self.tmpData[ixTmp][4] = last
            self.dialog.RefreshGrid(ixTmp, last, next)
        nxt = next[:19] if next else self.text.none
        self.updateLogFile(self.text.execut % (data[ix][1], nxt))
        if cmdline:
            self.updateLogFile(self.text.cmdLine % cmdline.decode(FSE))


    def UpdateEGscheduler(self):
        data = self.data
        tmpList = []
        sched_list = eg.scheduler.__dict__['heap']
        for sched in sched_list:
            if sched[1] == self.RadioSureScheduleRun:
                if sched[2][0] in [item[1] for item in data]:
                    tmpList.append(sched)
                else:
                    self.updateLogFile(self.text.cancAndDel % sched[2][0])
                    eg.scheduler.CancelTask(sched)
        sched_list = tmpList
        for schedule in data:
            startMoment = self.NextRun(schedule[2], schedule[3])
            if not startMoment:
                continue
            startTicks = mktime(strptime(startMoment,"%Y-%m-%d %H:%M:%S"))
            nameList = [item[2][0] for item in sched_list]
            if schedule[1] in nameList:
                sched = sched_list[nameList.index(schedule[1])]
                if not schedule[0]: # schedule is disabled !
                    eg.scheduler.CancelTask(sched)
                    self.updateLogFile(self.text.cancAndDis % schedule[1])
                elif sched[0] != startTicks: #Re-schedule
                    self.updateLogFile(self.text.re_Sched % (schedule[1], startMoment))
                    eg.scheduler.CancelTask(sched)
                    eg.scheduler.AddTaskAbsolute(startTicks, self.RadioSureScheduleRun, schedule[1])
            elif schedule[0]: #New schedule
                    eg.scheduler.AddTaskAbsolute(startTicks, self.RadioSureScheduleRun, schedule[1])
                    self.updateLogFile(self.text.newSched % (schedule[1], startMoment))


    def dataToXml(self):
        impl = miniDom.getDOMImplementation()
        dom = impl.createDocument(None, u'Document', None)
        root = dom.documentElement
        commentNode = dom.createComment(self.text.xmlComment % str(dt.now())[:19])
        dom.insertBefore(commentNode, root)
        for item in self.data:
            schedNode = dom.createElement(u'Schedule')
            schedNode.setAttribute(u'Name', unicode(item[1]))
            schedNode.setAttribute(u'Type', unicode(item[2]))
            enableNode = dom.createElement(u'Enable')
            enableText = dom.createTextNode(unicode(item[0]))
            enableNode.appendChild(enableText)
            schedNode.appendChild(enableNode)
            last_runNode = dom.createElement(u'Last_run')
            last_runText = dom.createTextNode(unicode(item[4]))
            last_runNode.appendChild(last_runText)
            schedNode.appendChild(last_runNode)
            sourceNode = dom.createElement(u'Source')
            sourceText = dom.createTextNode(unicode(item[5]))
            sourceNode.appendChild(sourceText)
            schedNode.appendChild(sourceNode)
            filenameNode = dom.createElement(u'Filename')
            filenameText = dom.createTextNode(unicode(item[6]))
            filenameNode.appendChild(filenameText)
            schedNode.appendChild(filenameNode)
            modesNode = dom.createElement(u'Modes')
            modesText = dom.createTextNode(unicode(item[7]))
            modesNode.appendChild(modesText)
            schedNode.appendChild(modesNode)
            dateTimeNode = dom.createElement(u'Datetime')
            start_timeNode = dom.createElement(u'Start_time')
            start_timeText = dom.createTextNode(unicode(item[3][0]))
            start_timeNode.appendChild(start_timeText)
            dateTimeNode.appendChild(start_timeNode)
            durationNode = dom.createElement(u'Duration')
            durationText = dom.createTextNode(unicode(item[3][1]))
            durationNode.appendChild(durationText)
            dateTimeNode.appendChild(durationNode)
            if item[2] == 0:
                dateNode = dom.createElement(u'Date')
                dateText = dom.createTextNode(unicode(item[3][2]))
                dateNode.appendChild(dateText)
                dateTimeNode.appendChild(dateNode)
                yearlyNode = dom.createElement(u'Yearly')
                yearlyText = dom.createTextNode(unicode(item[3][3]))
                yearlyNode.appendChild(yearlyText)
                dateTimeNode.appendChild(yearlyNode)
            if item[2] == 2:
                weekdayNode = dom.createElement(u'Weekday')
                weekdayText = dom.createTextNode(unicode(item[3][2]))
                weekdayNode.appendChild(weekdayText)
                dateTimeNode.appendChild(weekdayNode)
                holidayNode = dom.createElement(u'HolidayCheck')
                holidayText = dom.createTextNode(unicode(item[3][4]))
                holidayNode.appendChild(holidayText)
                dateTimeNode.appendChild(holidayNode)
                holiday2Node = dom.createElement(u'HolidayCheck_2')
                holiday2Text = dom.createTextNode(unicode(item[3][3]))
                holiday2Node.appendChild(holiday2Text)
                dateTimeNode.appendChild(holiday2Node)
            if item[2] == 3:
                orderNode = dom.createElement(u'Order')
                orderText = dom.createTextNode(unicode(item[3][2]))
                orderNode.appendChild(orderText)
                dateTimeNode.appendChild(orderNode)
                weekdayNode = dom.createElement(u'Weekday')
                weekdayText = dom.createTextNode(unicode(item[3][3]))
                weekdayNode.appendChild(weekdayText)
                dateTimeNode.appendChild(weekdayNode)
                first_halfNode = dom.createElement(u'First_half')
                first_halfText = dom.createTextNode(unicode(item[3][4]))
                first_halfNode.appendChild(first_halfText)
                dateTimeNode.appendChild(first_halfNode)
                second_halfNode = dom.createElement(u'Second_half')
                second_halfText = dom.createTextNode(unicode(item[3][5]))
                second_halfNode.appendChild(second_halfText)
                dateTimeNode.appendChild(second_halfNode)
                holidayNode = dom.createElement(u'HolidayCheck')
                holidayText = dom.createTextNode(unicode(item[3][6]))
                holidayNode.appendChild(holidayText)
                dateTimeNode.appendChild(holidayNode)
            if item[2] == 4:
                q_1_Node = dom.createElement(u'Q_1')
                q_1_Text = dom.createTextNode(unicode(item[3][2]))
                q_1_Node.appendChild(q_1_Text)
                dateTimeNode.appendChild(q_1_Node)
                q_2_Node = dom.createElement(u'Q_2')
                q_2_Text = dom.createTextNode(unicode(item[3][3]))
                q_2_Node.appendChild(q_2_Text)
                dateTimeNode.appendChild(q_2_Node)
                q_3_Node = dom.createElement(u'Q_3')
                q_3_Text = dom.createTextNode(unicode(item[3][4]))
                q_3_Node.appendChild(q_3_Text)
                dateTimeNode.appendChild(q_3_Node)
                q_4_Node = dom.createElement(u'Q_4')
                q_4_Text = dom.createTextNode(unicode(item[3][5]))
                q_4_Node.appendChild(q_4_Text)
                dateTimeNode.appendChild(q_4_Node)
                first_halfNode = dom.createElement(u'First_half')
                first_halfText = dom.createTextNode(unicode(item[3][6]))
                first_halfNode.appendChild(first_halfText)
                dateTimeNode.appendChild(first_halfNode)
                second_halfNode = dom.createElement(u'Second_half')
                second_halfText = dom.createTextNode(unicode(item[3][7]))
                second_halfNode.appendChild(second_halfText)
                dateTimeNode.appendChild(second_halfNode)
            if item[2] == 5:
                dateNode = dom.createElement(u'Date')
                dateText = dom.createTextNode(unicode(item[3][2]))
                dateNode.appendChild(dateText)
                dateTimeNode.appendChild(dateNode)
                numberNode = dom.createElement(u'Number')
                numberText = dom.createTextNode(unicode(item[3][3]))
                numberNode.appendChild(numberText)
                dateTimeNode.appendChild(numberNode)
                unitNode = dom.createElement(u'Unit')
                unitText = dom.createTextNode(unicode(item[3][4]))
                unitNode.appendChild(unitText)
                dateTimeNode.appendChild(unitNode)
            schedNode.appendChild(dateTimeNode)
            root.appendChild(schedNode)
        f = file(u'%s\\Scheduler.xml' % self.xmlpath, 'wb')
        writer = lookup('utf-8')[3](f)
        dom.writexml(writer, encoding = 'utf-8')
        f.close()
        return f.closed


    def xmlToData(self):
        data = []
        xmlfile = u'%s\\Scheduler.xml' % self.xmlpath
        if not exists(xmlfile):
            return data
        xmldoc = miniDom.parse(xmlfile)
        document = xmldoc.getElementsByTagName('Document')[0]
        schedules = tuple(document.getElementsByTagName('Schedule'))
        for schedule in schedules:
            dataItem = []
            enable = int(schedule.getElementsByTagName('Enable')[0].firstChild.data)
            dataItem.append(enable)
            name = schedule.attributes["Name"].value
            dataItem.append(name)
            schedType = int(schedule.attributes["Type"].value)
            dataItem.append(schedType)
            dateTime = schedule.getElementsByTagName('Datetime')[0]
            params = []
            start_time = dateTime.getElementsByTagName('Start_time')[0].firstChild.data
            params.append(start_time)
            duration = dateTime.getElementsByTagName('Duration')[0].firstChild.data
            params.append(duration)
            if schedType == 0:
                date = dateTime.getElementsByTagName('Date')[0].firstChild.data
                params.append(date)
                date = int(dateTime.getElementsByTagName('Yearly')[0].firstChild.data)
                params.append(date)
            if schedType == 2:
                weekday = int(dateTime.getElementsByTagName('Weekday')[0].firstChild.data)
                params.append(weekday)
                holiday2 = int(dateTime.getElementsByTagName('HolidayCheck_2')[0].firstChild.data)
                params.append(holiday2)
                holiday = int(dateTime.getElementsByTagName('HolidayCheck')[0].firstChild.data)
                params.append(holiday)
            if schedType == 3:
                order = int(dateTime.getElementsByTagName('Order')[0].firstChild.data)
                params.append(order)
                weekday = int(dateTime.getElementsByTagName('Weekday')[0].firstChild.data)
                params.append(weekday)
                first_half = int(dateTime.getElementsByTagName('First_half')[0].firstChild.data)
                params.append(first_half)
                second_half = int(dateTime.getElementsByTagName('Second_half')[0].firstChild.data)
                params.append(second_half)
                holiday = int(dateTime.getElementsByTagName('HolidayCheck')[0].firstChild.data)
                params.append(holiday)
            if schedType == 4:
                q_1 = int(dateTime.getElementsByTagName('Q_1')[0].firstChild.data)
                params.append(q_1)
                q_2 = int(dateTime.getElementsByTagName('Q_2')[0].firstChild.data)
                params.append(q_2)
                q_3 = int(dateTime.getElementsByTagName('Q_3')[0].firstChild.data)
                params.append(q_3)
                q_4 = int(dateTime.getElementsByTagName('Q_4')[0].firstChild.data)
                params.append(q_4)
                first_half = int(dateTime.getElementsByTagName('First_half')[0].firstChild.data)
                params.append(first_half)
                second_half = int(dateTime.getElementsByTagName('Second_half')[0].firstChild.data)
                params.append(second_half)
            if schedType == 5:
                date = dateTime.getElementsByTagName('Date')[0].firstChild.data
                params.append(date)
                number = int(dateTime.getElementsByTagName('Number')[0].firstChild.data)
                params.append(number)
                unit = int(dateTime.getElementsByTagName('Unit')[0].firstChild.data)
                params.append(unit)
            dataItem.append(params)
            last_run = schedule.getElementsByTagName('Last_run')[0].firstChild
            last_run = last_run.data if last_run else " "
            dataItem.append(last_run)
            source = schedule.getElementsByTagName('Source')[0].firstChild
            source = source.data if source else ""
            dataItem.append(source)
            filename = schedule.getElementsByTagName('Filename')[0].firstChild
            filename = filename.data if filename else ""
            dataItem.append(filename)
            modes = schedule.getElementsByTagName('Modes')[0].firstChild.data
            dataItem.append(int(modes))
            data.append(dataItem)
        return data


    def GetStatusRS(self, hwnds = None):
        hwnds = hwnds or HandleRS()
        maxFav = None
        recording = None
        if hwnds:
            for hwnd in hwnds:
                try:
                    maxFav = SendMessageTimeout(hwnd, self.favMesg, 0, 0)
                    recording = SendMessageTimeout(hwnd, self.recMesg, 0, 0)
                except:
                    #raise
                    pass
                if maxFav is not None and recording is not None:
                    #pass
                    break
        if maxFav is not None and recording is not None:
            return (maxFav, recording)
        else:
            return (None, None)


    def GetNewHwnd(self, oldHwnds = [], src = None, hid = False, mut = False):
        hwnds = HandleRS()
        if len(hwnds) > 0 and self.GetOneInstance():
            wx.CallAfter(
                MessageBox,
                None,
                self.text.message3,
                self.text.messBoxTit1,
                wx.ICON_EXCLAMATION,
                15,
                plugin = self,
                )
            return []
        maxInst = 2 if (not self.maxFav or self.maxFav == 30) else 10
        if len(oldHwnds) >= maxInst:
            wx.CallAfter(
                MessageBox,
                None,
                self.text.message2 % maxInst,
                self.text.messBoxTit1,
                wx.ICON_EXCLAMATION,
                15,
                plugin = self,
                )
            return []
        i = 0
        hwnds = oldHwnds if oldHwnds else []
        rs = u'%s\\RadioSure.exe' % self.RadioSurePath
        rs = rs.encode(FSE) if isinstance(rs, unicode) else rs
        args = [rs, "/play"]
        if mut:
            args.append("/mute")
        if hid:
            args.append("/hidden")
        if src:
            args.append(u'/source="%s"' % src)
        if isfile(rs):
            Popen(args)
            while i < 100 and hwnds == oldHwnds:
                i += 1
                hwnds = HandleRS()
                sleep(0.1)
            sleep(1.5)
        return list(set(hwnds)-set(oldHwnds))


    def SetMaxFavs(self):
        maxFav = 30
        hwnds = HandleRS()
        if hwnds:
            maxFav, rec = self.GetStatusRS(hwnds)
            if not maxFav: # ToDo: kill process ???
                hwnds = self.GetNewHwnd(hwnds, hid = True, mut = True)
                if hwnds:
                    maxFav, rec = self.GetStatusRS(hwnds)
                    PostMessage(hwnds[0], WM_COMMAND, 1, 0) # Close
        else:
            hwnds = self.GetNewHwnd(hid = True, mut = True)
            if hwnds:
                maxFav, rec = self.GetStatusRS(hwnds)
                PostMessage(hwnds[0], WM_COMMAND, 1, 0) # Close
        self.maxFav = maxFav

    def __init__(self):
        self.observThread = None
        text=Text
        self.AddActionsFromList(ACTIONS)

    def GetLabel(
        self,
        path = None,
        xmlpath = None,
        logfile = None,
        holidays = [[], []],
        first_day = 0,
    ):
        if not self.submenus:
            self.RadioSurePath = path
            self.xmlpath = xmlpath
            self.submenus = self.GetSubmenuStrings()
        return self.name


    def __start__(
        self,
        path = None,
        xmlpath = None,
        logfile = None,
        holidays = [[], []],
        first_day = 0,
        ):

        self.recMesg = RegisterWindowMessage("WM_RADIOSURE_GET_RECORDING_STATUS")
        self.favMesg = RegisterWindowMessage("WM_RADIOSURE_GET_MAX_FAVORITES")

        if not self.submenus:
            self.submenus = self.GetSubmenuStrings()
        self.RadioSurePath = path
        self.xmlpath = xmlpath
        wx.CallAfter(self.SetMaxFavs)
        self.logfile = logfile
        self.holidays = holidays
        self.first_day = first_day
        self.data = []
        self.tmpData = []
        if self.xmlpath:
            if exists(self.xmlpath):
                self.data = self.xmlToData()
            if logfile:
                 self.updateLogFile(self.text.start, True)
            self.UpdateEGscheduler()


    def __stop__(self):
        if self.dataToXml():
            self.updateLogFile("File Scheduler.xml saved")
        if self.observThread:
            ot = self.observThread
            if ot.isAlive():
                ot.AbortObservation()
            del self.observThread
        self.observThread = None
        sched_list = eg.scheduler.__dict__['heap']
        tmpLst = []
        for sched in sched_list:
            if sched[1] == self.RadioSureScheduleRun:
                tmpLst.append(sched)
        if len(tmpLst) > 0:
            self.updateLogFile(self.text.stop)
            for sched in tmpLst:
                eg.scheduler.CancelTask(sched)
                self.updateLogFile(self.text.canc % sched[2][0])
        if self.dialog:
            self.dialog.onClose(wx.CommandEvent())
        if self.manager:
            self.manager.onClose(wx.CommandEvent())


    def __close__(self):
        if self.observThread:
            ot = self.observThread
            if ot.isAlive():
                ot.AbortObservation()


    def Configure(
        self,
        path = "",
        xmlpath = "",
        logfile = None,
        holidays = [[], []],
        first_day = 0,
        ):
        panel = eg.ConfigPanel(self)
        panel.holidays = cpy(holidays)
        del holidays
        managerButton = wx.Button(panel, -1, self.text.managerButton)
        if not path: #First run after plugin insert
            managerButton.Enable(False)
        self.RadioSurePath = path
        self.xmlpath = xmlpath
        self.logfile = logfile
        self.first_day = first_day
        label1Text = wx.StaticText(panel, -1, self.text.label1)
        rsPathCtrl = MyDirBrowseButton(
            panel,
            toolTip = self.text.toolTipFolder,
            dialogTitle = self.text.browseTitle,
            buttonText = eg.text.General.browse
        )
        rsPathCtrl.GetTextCtrl().SetEditable(False)
        label2Text = wx.StaticText(panel, -1, self.text.label2)
        xmlPathCtrl = MyDirBrowseButton(
            panel,
            toolTip = self.text.toolTipFolder,
            dialogTitle = self.text.browseTitle,
            buttonText = eg.text.General.browse
        )
        xmlPathCtrl.GetTextCtrl().SetEditable(False)
        logFileCtrl = MyFileBrowseButton(
            panel,
            toolTip = self.text.toolTipFile,
            dialogTitle = self.text.browseFile,
            buttonText = eg.text.General.browse
        )
        logFileCtrl.GetTextCtrl().SetEditable(False)
        logCheckBox = wx.CheckBox(panel, -1, self.text.logLabel)
        if not self.RadioSurePath or not exists(self.RadioSurePath):
            RSpath = getPathFromReg() #Try get path from registry
            if RSpath: #Regular installation
                if exists(RSpath):
                    self.RadioSurePath = RSpath
            else: #Portable installation
                self.RadioSurePath = u"%s\\RadioSure" % unicode(eg.folderPath.LocalAppData)
            xmlPath = u"%s\\RadioSure" % unicode(eg.folderPath.LocalAppData)
            if exists(xmlPath):
                self.xmlpath = xmlPath
        if exists(join(self.RadioSurePath, "RadioSure.exe")):
            rsPathCtrl.GetTextCtrl().ChangeValue(self.RadioSurePath)
            rsPathCtrl.Enable(False)
            label1Text.Enable(False)
        if exists(join(self.xmlpath, "RadioSure.xml")):
            xmlPathCtrl.GetTextCtrl().ChangeValue(self.xmlpath)
            xmlPathCtrl.Enable(False)
            label2Text.Enable(False)


        def NotHidden():
            try:
                nssh = OpenKey(
                    HKEY_CURRENT_USER,
                    "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced"
                )
                res = QueryValueEx(nssh, "Hidden")[0] != 2
                CloseKey(nssh)
            except:
                res = False
            return res


        def NotHiddenAttr(path):
            attr = GetFileAttributes(path)
            if attr & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM):
                return False
            else:
                p = split(path)[0]
                if len(p) > 3:
                    return NotHiddenAttr(p)
                return True

        if self.logfile is None:
            logCheckBox.SetValue(True)
            if NotHiddenAttr(self.xmlpath) or NotHidden():
                self.logfile = u'%s\\RadioSureSchedulerLog.txt' % self.xmlpath
            else:
                self.logfile = u'%s\\RadioSureSchedulerLog.txt' % unicode(eg.folderPath.Documents)
        else:
            val = self.logfile != ""
            logCheckBox.SetValue(val)
            logFileCtrl.Enable(val)
        logFileCtrl.GetTextCtrl().ChangeValue(self.logfile)
        rsPathCtrl.startDirectory = self.RadioSurePath
        xmlPathCtrl.startDirectory = self.xmlpath
        logFileCtrl.startDirectory = self.logfile or u"%s\\RadioSure" % unicode(eg.folderPath.LocalAppData)
        sizerAdd = panel.sizer.Add
        sizerAdd(label1Text, 0)
        sizerAdd(rsPathCtrl,0,wx.TOP|wx.EXPAND,2)
        sizerAdd(label2Text, 0, wx.TOP,15)
        sizerAdd(xmlPathCtrl,0,wx.TOP|wx.EXPAND,2)
        sizerAdd(logCheckBox, 0, wx.TOP,15)
        sizerAdd(logFileCtrl, 0, wx.TOP|wx.EXPAND,2)
        firstDayLabel = wx.StaticText(panel, -1, self.text.first_day)
        firstDayCtrl = wx.Choice(
            panel,
            -1,
            choices = (
                day_name[0],
                day_name[6]
            ),
            size = (firstDayLabel.GetSize()[0], -1)
        )
        firstDayCtrl.SetSelection(first_day)
        panel.holidButton = wx.Button(panel, -1, self.text.holidButton)


        def OnApplyBtn(evt):
            managerButton.Enable(True)
            evt.Skip()
        panel.dialog.buttonRow.applyButton.Bind(wx.EVT_BUTTON, OnApplyBtn)


        def onManagerButton(evt):
            if not self.dialog:
                wx.CallAfter(SchedulerDialog, self.text.OpenScheduler, self)
            else:
                if self.dialog.GetPosition() == (-32000, -32000):
                    ShowWindow(self.dialog.GetHandle(), SW_RESTORE)
                wx.CallAfter(self.dialog.Raise)
            evt.Skip()
        managerButton.Bind(wx.EVT_BUTTON, onManagerButton)


        def onHolidButton(evt):
            dlg = HolidaysFrame(
                parent = panel,
                plugin = self,
            )
            dlg.Centre()
            wx.CallAfter(dlg.ShowHolidaysFrame)
            evt.Skip()
        panel.holidButton.Bind(wx.EVT_BUTTON, onHolidButton)
        bottomSizer = wx.GridBagSizer(1, 1)
        bottomSizer.Add(firstDayLabel, (0, 0), flag = wx.LEFT)
        bottomSizer.Add(firstDayCtrl, (1, 0), flag = wx.LEFT)
        bottomSizer.Add((1, -1), (1, 1), flag = wx.EXPAND)
        bottomSizer.Add((1, -1), (1, 3), flag = wx.EXPAND)
        bottomSizer.Add(panel.holidButton, (1, 2))
        bottomSizer.Add(managerButton, (1, 4), flag = wx.RIGHT)
        bottomSizer.AddGrowableCol(1,1)
        bottomSizer.AddGrowableCol(3,1)
        sizerAdd(bottomSizer, 1, wx.TOP | wx.EXPAND, 15)


        def Validation():
            flag1 = "%s\\RadioSure.exe" % exists(rsPathCtrl.GetValue())
            flag2 = "%s\\RadioSure.xml" % exists(xmlPathCtrl.GetValue())
            flag3 = logCheckBox.IsChecked() and logFileCtrl.GetValue() != "" or not logCheckBox.IsChecked()
            flag = flag1 and flag2 and flag3
            panel.dialog.buttonRow.okButton.Enable(flag)
            panel.isDirty = True
            panel.dialog.buttonRow.applyButton.Enable(flag)


        def OnPathChange(event):
            path = rsPathCtrl.GetValue()
            if not exists("%s\\RadioSure.exe" % path):
                MessageBox(
                    panel,
                    self.text.boxMessage1 % 'RadioSure.exe',
                    self.text.boxTitle % path,
                    wx.ICON_EXCLAMATION,
                    plugin = self
                    )
            if path != "":
                rsPathCtrl.startDirectory = path
                self.RadioSurePath = path
            Validation()
            event.Skip()
        rsPathCtrl.Bind(wx.EVT_TEXT, OnPathChange)


        def OnPath2Change(event):
            path2 = xmlPathCtrl.GetValue()
            if not exists("%s\\RadioSure.xml" % path2):
                MessageBox(
                    panel,
                    self.text.boxMessage1 % 'RadioSure.xml',
                    self.text.boxTitle % path2,
                    wx.ICON_EXCLAMATION,
                    plugin = self
                    )
            if path2 != "":
                self.xmlpath = path2
                xmlPathCtrl.startDirectory = path2
            Validation()
            event.Skip()
        xmlPathCtrl.Bind(wx.EVT_TEXT, OnPath2Change)


        def logFileChange(event):
            self.logfile = logFileCtrl.GetValue()
            tmpVal = self.logfile
            if not tmpVal:
                tmpPath = u"%s\\RadioSure" % unicode(eg.folderPath.LocalAppData)
                tmpVal = tmpPath if exists(tmpPath) else self.RadioSurePath
            logFileCtrl.startDirectory = tmpVal
            Validation()
            event.Skip()
        logFileCtrl.Bind(wx.EVT_TEXT, logFileChange)


        def onLogCheckBox(evt):
            val = evt.IsChecked()
            logFileCtrl.Enable(val)
            if not val:
                logFileCtrl.SetValue("")
            else:
                Validation()
            evt.Skip()
        logCheckBox.Bind(wx.EVT_CHECKBOX, onLogCheckBox)
        while panel.Affirmed():
            panel.SetResult(
                rsPathCtrl.GetValue(),
                xmlPathCtrl.GetValue(),
                logFileCtrl.GetValue(),
                panel.holidays,
                firstDayCtrl.GetSelection(),
            )
#===============================================================================
#cls types for Actions list:
#===============================================================================

class Run(eg.ActionBase):

    class text:
        play = "Automatically play selected favorite after start"
        default = "Use start settings RadioSure"
        label = "Select favorite:"
        over = "Too large number (%s > %s) !"
        alr_run = "RadioSure is already running !"


    def __call__(self, play = False, fav = 1):

        def Play(hwnds):
                self.plugin.RefreshVariables()
                if fav <= len(self.plugin.Favorites):
                    if play:
                        SendMessage(hwnds[0], WM_COMMAND, 4101+fav, 0)
                    return str(fav)+": "+self.plugin.Favorites[self.plugin.FavIx][1]
                else:
                    return self.text.over % (str(fav),\
                        str(len(self.plugin.Favorites)))
        hwnds = HandleRS()
        if not hwnds:
            hwnds = self.plugin.GetNewHwnd()
            if hwnds:
                return Play(hwnds)
            else:
                self.PrintError(self.plugin.text.text1)
                return self.plugin.text.text1
        elif play:
            for hwnd in hwnds:
                x, rec = self.plugin.GetStatusRS([hwnd])
                if rec != 1:
                    SendMessage(hwnd, WM_COMMAND, 4101+fav, 0)
                    break
            if rec or rec is None:
                hwnds = self.plugin.GetNewHwnd(hwnds)
                if hwnds:
                    return Play(hwnds)
        else:
            self.PrintError(self.text.alr_run)
            return self.text.alr_run


    def GetLabel(self, play, fav):
        num = str(fav) if play else ''
        return "%s:  %s" % (self.name, num)


    def Configure(self, play = False, fav = 1):
        panel=eg.ConfigPanel(self)
        sizerAdd=panel.sizer.Add
        rb1 = panel.RadioButton(play, self.text.play, style=wx.RB_GROUP)
        rb2 = panel.RadioButton(not play, self.text.default)
        sizerAdd(rb1,0,wx.TOP,15)
        sizerAdd(rb2,0,wx.TOP,6)
        favLbl=wx.StaticText(panel, -1, self.text.label)
        sizerAdd(favLbl,0,wx.TOP,25)
        favCtrl = eg.SpinNumCtrl(
            panel,
            -1,
            fav,
            fractionWidth=0,
            min=1,
            max=30,
        )
        favCtrl.SetValue(fav)
        sizerAdd(favCtrl,0,wx.TOP,5)

        def onChangeMode(evt=None):
            enbl=rb1.GetValue()
            favLbl.Enable(enbl)
            favCtrl.Enable(enbl)
            if evt is not None:
                evt.Skip()
        rb1.Bind(wx.EVT_RADIOBUTTON, onChangeMode)
        rb2.Bind(wx.EVT_RADIOBUTTON, onChangeMode)
        onChangeMode()

        while panel.Affirmed():
            panel.SetResult(
                rb1.GetValue(),
                favCtrl.GetValue()
            )
#===============================================================================

class WindowControl(eg.ActionBase):
    def __call__(self):
        hwnd = HandleRS()
        if hwnd:
            SendMessage(hwnd[0], WM_SYSCOMMAND, self.value, 0)
        else:
            self.PrintError(self.plugin.text.text1)
            return self.plugin.text.text1
#===============================================================================

class SendMessageActions(eg.ActionBase):
    def __call__(self):
        hwnd = HandleRS()
        if hwnd:
            SendMessage(hwnd[0], WM_COMMAND, self.value, 0)
        else:
            self.PrintError(self.plugin.text.text1)
            return self.plugin.text.text1
#===============================================================================

class MinimRest(eg.ActionBase):
    def __call__(self):
        hwnd = HandleRS()
        if hwnd:
            winState = GetWindowPlacement(hwnd[0])[1]
            if winState == 1:
                SendMessage(hwnd[0], WM_COMMAND, 1075, 0)
            elif winState == 2:
                SendMessage(hwnd[0], WM_SYSCOMMAND, SC_RESTORE, 0)
        else:
            self.PrintError(self.plugin.text.text1)
            return self.plugin.text.text1
#===============================================================================

class CheckAndChange(eg.ActionBase):
    def __call__(self):
        hwnd = HandleRS()
        if hwnd:
            status = self.plugin.GetRS_Status(hwnd[0])
            if status[self.value[0]] == self.value[1]:
                SendMessage(hwnd[0], WM_COMMAND, self.value[2], 0)
#===============================================================================

class GetStatus(eg.ActionBase):
    def __call__(self):
        hwnd = HandleRS()
        if hwnd:
            status = self.plugin.GetRS_Status(hwnd[0])
            return status[self.value]
#===============================================================================

class GetMenuItem(eg.ActionBase):
    def __call__(self):
        hwnd = HandleRS()
        if hwnd:
            return self.plugin.GetMenuItem(hwnd[0], self.value)
#===============================================================================

class SetVolume(eg.ActionBase):

    class text:
        label=["Set volume (0 - 100%):",
            "Set step (1 - 25%):",
            "Set step (1 - 25%):"]


    def __call__(self, step = None):
        if step is None:
            if self.value == 0:
                step = 50
            else:
                step = 5
        hwnd = GetCtrlByID(1006) #1006 = ID of ctrl "msctls_trackbar32"
        if hwnd:
            vol = SendMessage(hwnd, TBM_GETPOS, 0, 0)
            key = None
            value = None
            if self.value == 0:
                volume = step
            elif self.value == 1:
                volume = vol+step if (vol+step)<100 else 100
            else:
                volume = vol-step if (vol-step)>0 else 0
            if vol>volume:
                key='{Left}'
                if vol>volume+1:
                    value = volume+1
            elif vol<volume:
                key='{Right}'
                if vol<volume-1:
                    value = volume-1
            if value:
                SendMessage(hwnd, TBM_SETPOS,1,value)
            if key:
                eg.SendKeys(hwnd, key, False)
            return SendMessage(hwnd, TBM_GETPOS, 0, 0)
        else:
            self.PrintError(self.plugin.text.text1)
            return self.plugin.text.text1


    def Configure(self, step = None):
        if step is None:
            if self.value == 0:
                step = 50
            else:
                step = 5
        panel=eg.ConfigPanel(self)
        panel.sizer.Add(wx.StaticText(panel, -1, self.text.label[self.value]))
        if self.value == 0:
            Min = 0
            Max = 100
        else:
            Min = 1
            Max = 25
        volumeCtrl = eg.SpinNumCtrl(
            panel,
            -1,
            step,
            fractionWidth=0,
            increment=1,
            min=Min,
            max=Max,
        )
        volumeCtrl.SetValue(step)
        panel.sizer.Add(volumeCtrl,0,wx.TOP,10)

        while panel.Affirmed():
            panel.SetResult(volumeCtrl.GetValue())
#===============================================================================

class GetVolume(eg.ActionBase):

    def __call__(self):
        hwnd = GetCtrlByID(1006)  #1006 = ID for ctrl "msctls_trackbar32"
        if hwnd:
            return SendMessage(hwnd, TBM_GETPOS, 0, 0)
        else:
            self.PrintError(self.plugin.text.text1)
            return self.plugin.text.text1
#===============================================================================

class SelectFav(eg.ActionBase):

    class text:
        label = "Select preset number (1-30):"
        txtLabel = 'Preset number:'
        over = "Too large number (%s > %s) !"
        modeLabel = 'Preset number to get as:'
        modeChoices = [
            'Event payload',
            'Python expression',
            'Number'
        ]


    def __call__(self,fav = 1, mode = 0, number = '{eg.event.payload}'):
        hwnd = HandleRS()
        if hwnd:
            if mode == 2:
                indx = fav
            else:
                indx = int(eg.ParseString(number))
            self.plugin.RefreshVariables()
            if indx <= len(self.plugin.Favorites):
                SendMessage(hwnd[0], WM_COMMAND, 4101+indx, 0)
                return str(indx)+": "+self.plugin.Favorites[indx-1][1]
            else:
                self.PrintError(
                    self.text.over % (str(indx),str(len(self.plugin.Favorites))))
                return self.text.over % (str(indx),str(len(self.plugin.Favorites)))
        else:
            self.PrintError(self.plugin.text.text1)
            return self.plugin.text.text1


    def GetLabel(self, fav, mode, number):
        return "%s %s" % (self.text.txtLabel, str(fav) if mode == 2 else number)


    def Configure(self, fav = 1, mode = 0, number = '{eg.event.payload}'):
        self.number = number
        panel = eg.ConfigPanel(self)
        radioBoxMode = wx.RadioBox(
            panel,
            -1,
            self.text.modeLabel,
            choices = self.text.modeChoices,
            style=wx.RA_SPECIFY_ROWS
        )
        radioBoxMode.SetSelection(mode)
        txtBoxLabel = wx.StaticText(panel, -1, self.text.txtLabel)
        numberCtrl = wx.TextCtrl(panel,-1,self.number)
        spinLabel = wx.StaticText(panel, -1, self.text.label)
        favCtrl = eg.SpinNumCtrl(
            panel,
            -1,
            fav,
            fractionWidth=0,
            min=1,
            max=30,
        )
        favCtrl.SetValue(fav)
        panel.sizer.Add(radioBoxMode, 0, wx.TOP,0)
        panel.sizer.Add(txtBoxLabel,0,wx.TOP,10)
        panel.sizer.Add(numberCtrl,0,wx.TOP,5)
        panel.sizer.Add(spinLabel,0,wx.TOP,10)
        panel.sizer.Add(favCtrl,0,wx.TOP,5)

        def onRadioBox(event = None):
            sel = radioBoxMode.GetSelection()
            txtBoxLabel.Enable(False)
            numberCtrl.Enable(False)
            spinLabel.Enable(False)
            favCtrl.Enable(False)
            if sel == 0:
                self.number = '{eg.event.payload}'
            elif sel == 1:
                txtBoxLabel.Enable(True)
                numberCtrl.Enable(True)
            else:
                self.number = favCtrl.GetValue()
                spinLabel.Enable(True)
                favCtrl.Enable(True)
            numberCtrl.ChangeValue(str(self.number))
            if event:
                event.Skip()
        radioBoxMode.Bind(wx.EVT_RADIOBOX, onRadioBox)
        onRadioBox()


        def onSpin(event):
            numberCtrl.ChangeValue(str(favCtrl.GetValue()))
            event.Skip()
        favCtrl.Bind(wx.EVT_TEXT, onSpin)

        while panel.Affirmed():
            panel.SetResult(
                favCtrl.GetValue(),
                radioBoxMode.GetSelection(),
                numberCtrl.GetValue())
#===============================================================================

class NextPrevFav(eg.ActionBase):

    def __call__(self):
        hwnd = HandleRS()
        if hwnd:
            self.plugin.RefreshVariables()
            #ix = self.plugin.FavIx
            ix = self.plugin.GetMenuItem(hwnd[0], 7)[0]
            if self.value == 1 and ix == len(self.plugin.Favorites) - 1 :
                ix = -1
            elif self.value == -1 and ix == 0:
                ix = len(self.plugin.Favorites)
            SendMessage(hwnd[0], WM_COMMAND, 4102+ix+self.value, 0)
            return (str(ix+self.value+1)+": "+self.plugin.Favorites[ix+self.value][1])
        else:
            self.PrintError(self.plugin.text.text1)
            return self.plugin.text.text1
#===============================================================================

class RandomFav(eg.ActionBase):

    def __call__(self):
        hwnd = HandleRS()
        if hwnd:
            self.plugin.RefreshVariables()
            ix = self.plugin.GetMenuItem(hwnd[0], 7)[0]
            lng = len(self.plugin.Favorites)
            if lng > 1:
                newIx = randrange(lng)
                while newIx == ix:
                    newIx = randrange(lng)
                SendMessage(hwnd[0], WM_COMMAND, 4102+newIx, 0)
                return (str(newIx+1)+": "+self.plugin.Favorites[newIx][1])
        else:
            self.PrintError(self.plugin.text.text1)
            return self.plugin.text.text1
#===============================================================================

class GetPlayingTitle(eg.ActionBase):

    def __call__(self):
        hwnd = HandleRS()
        if hwnd:
            return GetWindowText(hwnd[0])
        else:
            self.PrintError(self.plugin.text.text1)
            return self.plugin.text.text1
#===============================================================================

class StartTitlebarObservation(eg.ActionBase):

    class text:
        intervalLabel = "Refresh interval (s):"
        label = "Tilebar event suffix (empty = no event):"
        label2 = "Station event suffix (empty = no event):"
        toolSuffix = """Here you specify the desired event suffix.
If the string is empty, the event is not triggered."""


    def __call__(
        self,
        period = 1.0,
        evtName ="titlebar",
        evtName2 ="station",
    ):
        if evtName or evtName2:
            if self.plugin.observThread:
                ot = self.plugin.observThread
                if ot.isAlive():
                    ot.AbortObservation()
                del self.plugin.observThread
            ot = ObservationThread(
                period,
                evtName,
                evtName2,
                self.plugin
            )
            ot.start()
            self.plugin.observThread = ot


    def GetLabel(
        self,
        period = 1.0,
        evtName ="",
        evtName2 ="",
    ):
        return "%s: %.1fs, %s, %s" % (self.name, period, evtName, evtName2)

    def Configure(
        self,
        period = 1.0,
        evtName = "titlebar",
        evtName2 ="station",
    ):
        panel = eg.ConfigPanel()
        periodNumCtrl = eg.SpinNumCtrl(
            panel,
            -1,
            period,
            integerWidth = 5,
            fractionWidth = 1,
            allowNegative = False,
            min = 0.1,
            increment = 0.1,
        )
        intervalLbl = wx.StaticText(panel, -1, self.text.intervalLabel)
        textLabel = wx.StaticText(panel, -1, self.text.label)
        textControl = wx.TextCtrl(panel, -1, evtName, size = (200,-1))
        textControl.SetToolTipString(self.text.toolSuffix)
        textLabel2 = wx.StaticText(panel, -1, self.text.label2)
        textControl2 = wx.TextCtrl(panel, -1, evtName2, size = (200,-1))
        textControl2.SetToolTipString(self.text.toolSuffix)
        AddCtrl = panel.sizer.Add
        AddCtrl(intervalLbl, 0, wx.TOP, 20)
        AddCtrl(periodNumCtrl, 0, wx.TOP, 3)
        AddCtrl(textLabel, 0, wx.TOP, 20)
        AddCtrl(textControl, 0, wx.TOP, 3)
        AddCtrl(textLabel2, 0, wx.TOP, 20)
        AddCtrl(textControl2, 0, wx.TOP, 3)
        textLabel.SetFocus()

        while panel.Affirmed():
            panel.SetResult(
            periodNumCtrl.GetValue(),
            textControl.GetValue(),
            textControl2.GetValue(),
        )
#===============================================================================

class StopTitlebarObservation(eg.ActionBase):

    def __call__(self):
        if self.plugin.observThread:
            ot = self.plugin.observThread
            if ot.isAlive():
                ot.AbortObservation()
            del self.plugin.observThread
        self.plugin.observThread = None
#===============================================================================

class OpenManager(eg.ActionBase):

    def __call__(self):
        if not self.plugin.manager:
            wx.CallAfter(ManagerDialog, self.text, self.plugin)
        else:
            if self.plugin.manager.GetPosition() == (-32000, -32000):
                ShowWindow(self.plugin.manager.GetHandle(), SW_RESTORE)
            wx.CallAfter(self.plugin.manager.Raise)
#===============================================================================

class HideManager(eg.ActionBase):

    def __call__(self):
        if self.plugin.manager:
            wx.CallAfter(self.plugin.manager.Close)
#===============================================================================

class GetFavorites(eg.ActionBase):

    def __call__(self):
        self.plugin.RefreshVariables()
        return self.plugin.Favorites
#===============================================================================

class OpenScheduler(eg.ActionBase):

    def __call__(self):
        if not self.plugin.dialog:
            wx.CallAfter(SchedulerDialog, self.text, self.plugin)
        else:
            if self.plugin.dialog.GetPosition() == (-32000, -32000):
                ShowWindow(self.plugin.dialog.GetHandle(), SW_RESTORE)
            wx.CallAfter(self.plugin.dialog.Raise)
#===============================================================================

class HideScheduler(eg.ActionBase):

    def __call__(self):
        if self.plugin.dialog:
            wx.CallAfter(self.plugin.dialog.Close)
#===============================================================================

class EnableSchedule(eg.ActionBase):

    class text:
        scheduleTitle = "Schedule title:"
        notFound = 'Can not find schedule "%s" !'


    def __call__(self, schedule=""):
        schedule = eg.ParseString(schedule)
        xmlfile = u'%s\\Scheduler.xml' % self.plugin.xmlpath
        if not exists(xmlfile):
            return
        data = self.plugin.data
        tmpLst = [item[1] for item in data]
        if schedule in tmpLst:
            ix = tmpLst.index(schedule)
            if self.value > -1:
                data[ix][0] = self.value
                self.plugin.dataToXml()
                self.plugin.UpdateEGscheduler()
                if self.plugin.dialog:
                    wx.CallAfter(self.plugin.dialog.EnableSchedule, schedule, self.value)
            return data[tmpLst.index(schedule)]
        else:
            return self.text.notFound % schedule


    def Configure(self, schedule=""):
        panel = eg.ConfigPanel()
        xmlfile = u'%s\\Scheduler.xml' % self.plugin.xmlpath
        if not exists(xmlfile):
            return
        data = self.plugin.xmlToData()
        choices = [item[1] for item in data]
        textControl = wx.ComboBox(panel, -1, schedule, size = (300,-1), choices = choices)
        panel.sizer.Add(wx.StaticText(panel,-1,self.text.scheduleTitle), 0,wx.LEFT|wx.TOP, 10)
        panel.sizer.Add(textControl, 0, wx.LEFT, 10)

        while panel.Affirmed():
            panel.SetResult(textControl.GetValue())
#===============================================================================

class EnableAll(eg.ActionBase):

    def __call__(self):
        xmlfile = u'%s\\Scheduler.xml' % self.plugin.xmlpath
        if not exists(xmlfile):
            return
        data = self.plugin.data
        for schedule in data:
            schedule[0] = self.value
        self.plugin.dataToXml()
        self.plugin.UpdateEGscheduler()
        if self.plugin.dialog:
            wx.CallAfter(self.plugin.dialog.EnableAll, self.value)
#===============================================================================

class DeleteSchedule(eg.ActionBase):

    class text:
        scheduleTitle = "Schedule title:"


    def __call__(self, schedule=""):
        schedule = eg.ParseString(schedule)
        xmlfile = u'%s\\Scheduler.xml' % self.plugin.xmlpath
        if not exists(xmlfile):
            return
        data = self.plugin.data
        tmpLst = [item[1] for item in data]
        if schedule in tmpLst:
            ix = tmpLst.index(schedule)
            data.pop(ix)
            self.plugin.dataToXml()
            self.plugin.UpdateEGscheduler()
            if self.plugin.dialog:
                wx.CallAfter(self.plugin.dialog.DeleteSchedule, schedule)


    def Configure(self, schedule=""):
        panel = eg.ConfigPanel()
        xmlfile = u'%s\\Scheduler.xml' % self.plugin.xmlpath
        if not exists(xmlfile):
            return
        data = self.plugin.xmlToData()
        choices = [item[1] for item in data]
        textControl = wx.ComboBox(panel, -1, schedule, size = (300,-1), choices = choices)
        panel.sizer.Add(wx.StaticText(panel,-1,self.text.scheduleTitle), 0,wx.LEFT|wx.TOP, 10)
        panel.sizer.Add(textControl, 0, wx.LEFT, 10)

        while panel.Affirmed():
            panel.SetResult(textControl.GetValue())
#===============================================================================

class RunScheduleImmediately(eg.ActionBase):

    class text:
        scheduleTitle = "Schedule title:"
        notFound = 'Can not find schedule "%s" !'
        immedRun = 'Schedule "%s" - IMMEDIATELY execution. Possible next time: %s'

    def __call__(self, schedule=""):
        schedule = eg.ParseString(schedule)
        data = self.plugin.data
        tmpLst = [item[1] for item in data]
        if schedule in tmpLst:
            ix = tmpLst.index(schedule)
            sched = self.plugin.data[ix]
            if sched[0]:
                for sch in eg.scheduler.__dict__['heap']:
                    if sch[1] == self.plugin.RadioSureScheduleRun:
                        if sch[2][0] == sched[1]:
                            eg.scheduler.CancelTask(sch)
                            self.plugin.updateLogFile(self.plugin.text.canc % sch[2][0])
                            break
                next, cmdline = self.plugin.Execute(sched, True)
                next = next[:19] if next else self.plugin.text.none
                self.plugin.updateLogFile(self.text.immedRun % (sched[1], next))
                if cmdline:
                    self.plugin.updateLogFile(self.plugin.text.cmdLine % cmdline.decode(FSE))
        else:
            self.PrintError(self.text.notFound % schedule)
            return self.text.notFound % schedule


    def Configure(self, schedule = ""):
        panel = eg.ConfigPanel()
        data = self.plugin.data
        choices = [item[1] for item in data]
        textControl = wx.ComboBox(panel, -1, schedule, size = (300, -1), choices = choices)
        panel.sizer.Add(wx.StaticText(panel, -1, self.text.scheduleTitle), 0, wx.LEFT | wx.TOP, 10)
        panel.sizer.Add(textControl, 0, wx.LEFT, 10)

        while panel.Affirmed():
            panel.SetResult(textControl.GetValue())
#===============================================================================

class AddSchedule(eg.ActionBase):

    class text:
        python_expr = "Python expression:"
        descr = u'''<rst>**Add schedule**.

In the edit box, enter a python expression with the parameters of the plan.
This may be for example *eg.result*, *eg.event.payload* or the entire list
(in the same format, what you get as a result of actions **"GetSchedule"**).

This action works in two ways (depending on the title of the schedule):

1. If a schedule with the same title already exists, its parameters are overwritten by new ones.
2. If the title does not exist yet, the schedule is added to the list as new.'''

    def __call__(self, expr = ""):
        schedule = eg.ParseString(expr)
        schedule = eval(schedule)
        if len(schedule) == 8 and isinstance(schedule[1], unicode):
            data = self.plugin.data
            tmpLst = [item[1] for item in data]
            if schedule[1] in tmpLst:
                data[tmpLst.index(schedule[1])] = cpy(schedule)
            else:
                data.append(schedule)
            self.plugin.UpdateEGscheduler()
            if self.plugin.dialog:
                wx.CallAfter(self.plugin.dialog.AddSchedule, schedule)


    def Configure(self, expr=""):
        panel = eg.ConfigPanel(resizable=True)
        textControl = wx.TextCtrl(panel, -1, expr, size = (300,-1), style = wx.TE_MULTILINE )
        panel.sizer.Add(wx.StaticText(panel,-1,self.text.python_expr), 0,wx.LEFT|wx.TOP, 10)
        panel.sizer.Add(textControl, 1, wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.EXPAND, 10)
        while panel.Affirmed():
            panel.SetResult(textControl.GetValue())
#===============================================================================

class ShowMenu(eg.ActionClass):

    name = "Show Radio Sure menu"
    description = "Show Radio Sure menu."
    panel = None

    class text:
        OSELabel = 'Menu show on:'
        menuPreview = 'RS On Screen Menu preview:'
        menuFont = 'Font:'
        txtColour = 'Text colour'
        background = 'Background colour'
        txtColourSel = 'Selected text colour'
        backgroundSel = 'Selected background colour'
        dialog = "Events ..."
        btnToolTip = """Press this button to assign events to control the menu !!!"""
        evtAssignTitle = "Menu control - events assignement"
        events = (
            "Cursor up:",
            "Cursor down:",
            "Back from the (sub)menu:",
            "Submenu, or select an item:",
            "Cancel (Escape):",
        )
        inverted = "Use inverted colours"
        submenuLbl = "Show main menu or submenu:"


    def __call__(
        self,
        fore,
        back,
        fontInfo = TAHOMA_INFO,
        monitor = 0,
        foreSel = (180, 180, 180),
        backSel = (75, 75, 75),
        evtList = [],
        inverted = True,
        submenu = 0
    ):
        hwnd = HandleRS()
        if hwnd:
            if not self.plugin.menuDlg:
                event = CreateEvent(None, 0, 0, None)
                wx.CallAfter(
                    Menu,
                    fore,
                    back,
                    foreSel,
                    backSel,
                    fontInfo,
                    False,
                    self.plugin,
                    event,
                    monitor,
                    hwnd[0],
                    evtList,
                    (0, 7, 8, 9, 10, 11, 12, 14)[submenu],
                )
                eg.actionThread.WaitOnEvent(event)


    def GetLabel(
        self,
        fore,
        back,
        fontInfo,
        monitor,
        foreSel,
        backSel,
        evtList,
        inverted,
        submenu = 0
    ):
        return "%s: %s" % (self.name, self.plugin.submenus[submenu])


    def Configure(
        self,
        fore = (75, 75, 75),
        back = (180, 180, 180),
        fontInfo = TAHOMA_INFO,
        monitor = 0,
        foreSel = (180, 180, 180),
        backSel = (75, 75, 75),
        evtList = [[],[],[],[],[]],
        inverted = True,
        submenu = 0
    ):
        self.fontInfo = fontInfo
        self.fore = fore
        self.back = back
        self.foreSel = foreSel
        self.backSel = backSel
        self.oldSel=0
        self.inverted = inverted
        global panel
        panel = eg.ConfigPanel(self)
        panel.evtList = cpy(evtList)
        previewLbl=wx.StaticText(panel, -1, self.text.menuPreview)
        listBoxCtrl = MenuGrid(panel, 3)
        items = (("Blabla_1",0,True,804),
                 ("Blabla_2",1,False,804),
                 ("Blabla_3",2,False,-1),)
        listBoxCtrl.Set(items)
        listBoxCtrl.SetBackgroundColour(self.back)
        listBoxCtrl.SetForegroundColour(self.fore)
        listBoxCtrl.SetSelectionBackground(self.backSel)
        listBoxCtrl.SetSelectionForeground(self.foreSel)
        #Font button
        fontLbl=wx.StaticText(panel, -1, self.text.menuFont)
        fontButton = eg.FontSelectButton(panel, value = fontInfo)
        font = wx.FontFromNativeInfoString(fontInfo)
        for n in range(10,20):
            font.SetPointSize(n)
            fontButton.SetFont(font)
            hght = fontButton.GetTextExtent('X')[1]
            if hght > 20:
                break
        listBoxCtrl.SetDefaultCellFont(font)
        arial = wx.FontFromNativeInfoString(ARIAL_INFO)
        fontButton.SetFont(font)
        for n in range(1,1000):
            arial.SetPointSize(n)
            fontButton.SetFont(arial)
            h = fontButton.GetTextExtent(u"\u25a0")[1]
            if h > hght:
                break
        arial.SetPointSize(2*n/3)
        fontButton.SetFont(arial)
        w0 = 2 * fontButton.GetTextExtent(u"\u25a0")[0]
        attr = gridlib.GridCellAttr()
        attr.SetFont(arial)
        attr.SetAlignment(wx.ALIGN_CENTRE, wx.ALIGN_CENTRE)
        listBoxCtrl.SetColAttr(0,attr)
        for n in range(1,1000):
            arial.SetPointSize(n)
            fontButton.SetFont(arial)
            h = fontButton.GetTextExtent(u"\u25ba")[1]
            if h > hght:
                break
        arial.SetPointSize(n/2)
        fontButton.SetFont(arial)
        w2 = 2 * fontButton.GetTextExtent(u"\u25ba")[0]
        attr = gridlib.GridCellAttr()
        attr.SetFont(arial)
        attr.SetAlignment(wx.ALIGN_RIGHT, wx.ALIGN_CENTRE)
        listBoxCtrl.SetColAttr(2,attr)
        listBoxCtrl.SetDefaultRowSize(hght+4, True)
        displayChoice = eg.DisplayChoice(panel, monitor)
        w = displayChoice.GetSize()[0]
        OSElbl = wx.StaticText(panel, -1, self.text.OSELabel)
        useInvertedCtrl = wx.CheckBox(panel, -1, self.text.inverted)
        useInvertedCtrl.SetValue(inverted)
        subMenuLbl = wx.StaticText(panel, -1, self.text.submenuLbl)
        if self.plugin.submenus:
            choices = self.plugin.submenus
        else:
            choices = self.plugin.GetSubmenuStrings()
        subMenuCtrl = wx.Choice(panel, -1, choices = choices)
        subMenuCtrl.SetSelection(submenu)
        #Button Text Colour
        foreLbl=wx.StaticText(panel, -1, self.text.txtColour+':')
        foreColourButton = eg.ColourSelectButton(panel,fore,title = self.text.txtColour)
        #Button Background Colour
        backLbl=wx.StaticText(panel, -1, self.text.background+':')
        backColourButton = eg.ColourSelectButton(panel,back,title = self.text.background)
        #Button Selected Text Colour
        foreSelLbl=wx.StaticText(panel, -1, self.text.txtColourSel+':')
        foreSelColourButton = eg.ColourSelectButton(panel,foreSel,title = self.text.txtColourSel)
        #Button Selected Background Colour
        backSelLbl=wx.StaticText(panel, -1, self.text.backgroundSel+':')
        backSelColourButton = eg.ColourSelectButton(panel,backSel,title = self.text.backgroundSel)
        #Button Dialog "Menu control - assignement of events"
        dialogButton = wx.Button(panel,-1,self.text.dialog)
        dialogButton.SetToolTipString(self.text.btnToolTip)
        foreSelLbl.Enable(not inverted)
        foreSelColourButton.Enable(not inverted)
        backSelLbl.Enable(not inverted)
        backSelColourButton.Enable(not inverted)
        #Sizers
        mainSizer = panel.sizer
        topSizer=wx.GridBagSizer(2, 30)
        mainSizer.Add(topSizer)
        topSizer.Add(previewLbl,(0, 0),flag = wx.TOP,border = 0)
        topSizer.Add(listBoxCtrl,(1, 0),(4, 1))
        topSizer.Add(useInvertedCtrl,(6, 0),flag = wx.TOP, border = 8)
        topSizer.Add(subMenuLbl,(8, 0), flag = wx.TOP,border = 8)
        topSizer.Add(subMenuCtrl,(9, 0), flag = wx.TOP)
        topSizer.Add(fontLbl,(0, 1),flag = wx.TOP)
        topSizer.Add(fontButton,(1, 1),flag = wx.TOP)
        topSizer.Add(foreLbl,(2, 1),flag = wx.TOP,border = 8)
        topSizer.Add(foreColourButton,(3, 1),flag = wx.TOP)
        topSizer.Add(backLbl,(4, 1),flag = wx.TOP,border = 8)
        topSizer.Add(backColourButton,(5, 1),flag = wx.TOP)
        topSizer.Add(OSElbl,(0, 2), flag = wx.TOP)
        topSizer.Add(displayChoice,(1, 2),flag = wx.TOP)
        topSizer.Add(foreSelLbl,(6, 1), (1, 2), flag = wx.TOP,border = 8)
        topSizer.Add(foreSelColourButton, (7, 1), flag = wx.TOP)
        topSizer.Add(backSelLbl,(8, 1), (1, 2), flag = wx.TOP,border = 8)
        topSizer.Add(backSelColourButton, (9, 1), flag = wx.TOP)
        topSizer.Add(dialogButton, (3, 2), flag = wx.TOP|wx.EXPAND)
        panel.sizer.Layout()
        wdth = 160
        if (hght+4)*listBoxCtrl.GetNumberRows() > listBoxCtrl.GetSize()[1]: #after Layout() !!!
            wdth -=  SYS_VSCROLL_X
        listBoxCtrl.SetColSize(0, w0)
        listBoxCtrl.SetColSize(1, wdth - w0 - w2)
        listBoxCtrl.SetColSize(2, w2)
        listBoxCtrl.SetGridCursor(-1, 1)
        listBoxCtrl.SelectRow(0)


        def OnMonitor(evt):
            listBoxCtrl.SetFocus()
            evt.Skip
        displayChoice.Bind(wx.EVT_CHOICE, OnMonitor)


        def OnInverted(evt):
            flag = evt.IsChecked()
            foreSelLbl.Enable(not flag)
            foreSelColourButton.Enable(not flag)
            backSelLbl.Enable(not flag)
            backSelColourButton.Enable(not flag)
            self.inverted = flag
            if flag:
                self.foreSel = self.back
                self.backSel = self.fore
                backSelColourButton.SetValue(self.backSel)
                foreSelColourButton.SetValue(self.foreSel)
                listBoxCtrl.SetSelectionForeground(self.foreSel)
                listBoxCtrl.SetSelectionBackground(self.backSel)
            listBoxCtrl.SetFocus()
            evt.Skip
        useInvertedCtrl.Bind(wx.EVT_CHECKBOX, OnInverted)


        def OnDialogBtn(evt):
            dlg = MenuEventsDialog(
                parent = panel,
                plugin = self.plugin,
            )
            dlg.Centre()
            wx.CallAfter(dlg.ShowMenuEventsDialog, self.text.evtAssignTitle, self.text.events)
            evt.Skip()
        dialogButton.Bind(wx.EVT_BUTTON, OnDialogBtn)


        def OnFontBtn(evt):
            value = evt.GetValue()
            self.fontInfo = value
            font = wx.FontFromNativeInfoString(value)
            for n in range(10,20):
                font.SetPointSize(n)
                fontButton.SetFont(font)
                hght = fontButton.GetTextExtent('X')[1]
                if hght > 20:
                    break
            listBoxCtrl.SetDefaultCellFont(font)
            listBoxCtrl.SetDefaultRowSize(hght+4, True)
            for i in range(listBoxCtrl.GetNumberRows()):
                listBoxCtrl.SetCellFont(i,1,font)
            listBoxCtrl.SetFocus()
            if evt:
                evt.Skip()
        fontButton.Bind(eg.EVT_VALUE_CHANGED, OnFontBtn)

        def OnColourBtn(evt):
            id = evt.GetId()
            value = evt.GetValue()
            if id == foreColourButton.GetId():
                listBoxCtrl.SetForegroundColour(value)
                if self.inverted:
                    self.backSel = self.fore
                    listBoxCtrl.SetSelectionBackground(value)
                    backSelColourButton.SetValue(value)
            elif id == backColourButton.GetId():
                listBoxCtrl.SetBackgroundColour(value)
                if self.inverted:
                    self.foreSel = self.back
                    listBoxCtrl.SetSelectionForeground(value)
                    foreSelColourButton.SetValue(value)
            elif id == foreSelColourButton.GetId():
                listBoxCtrl.SetSelectionForeground(value)
            elif id == backSelColourButton.GetId():
                listBoxCtrl.SetSelectionBackground(value)
            listBoxCtrl.Refresh()
            listBoxCtrl.SetFocus()
            evt.Skip()
        foreColourButton.Bind(eg.EVT_VALUE_CHANGED, OnColourBtn)
        backColourButton.Bind(eg.EVT_VALUE_CHANGED, OnColourBtn)
        foreSelColourButton.Bind(eg.EVT_VALUE_CHANGED, OnColourBtn)
        backSelColourButton.Bind(eg.EVT_VALUE_CHANGED, OnColourBtn)


        def setFocus():
            listBoxCtrl.SetFocus()
        panel.setFocus = setFocus

        # re-assign the test button
        def OnButton(event):
            hwnds = HandleRS()
            if hwnds:
                if not self.plugin.menuDlg:
                    wx.CallAfter(
                        Menu,
                        foreColourButton.GetValue(),
                        backColourButton.GetValue(),
                        foreSelColourButton.GetValue(),
                        backSelColourButton.GetValue(),
                        self.fontInfo,
                        True,
                        self.plugin,
                        CreateEvent(None, 0, 0, None),
                        displayChoice.GetSelection(),
                        hwnds[0],
                        panel.evtList,
                        (0, 7, 8, 9, 10, 11, 12, 14)[subMenuCtrl.GetSelection()]
                    )

        panel.dialog.buttonRow.testButton.Bind(wx.EVT_BUTTON, OnButton)

        while panel.Affirmed():
            fontInfo = fontButton.GetValue()
            if not fontInfo:
                font = listBoxCtrl.GetFont()
                font.SetPointSize(36)
                fontInfo = font.GetNativeFontInfoDesc()
            panel.SetResult(
            foreColourButton.GetValue(),
            backColourButton.GetValue(),
            fontInfo,
            displayChoice.GetSelection(),
            foreSelColourButton.GetValue(),
            backSelColourButton.GetValue(),
            panel.evtList,
            useInvertedCtrl.GetValue(),
            subMenuCtrl.GetSelection()
        )
#===============================================================================

ACTIONS = (
    (Run,"Run","Run RadioSure","Run RadioSure with its default settings.",None),
    (SendMessageActions,"Close","Close window (exit RadioSure)","Close window (exit RadioSure).",1),
    (GetPlayingTitle,"GetPlayingTitle","Get currently playing station/title","Gets the name of currently playing station/title.", None),
    (StartTitlebarObservation,"StartTitlebarObservation","Start periodical observation","Starts periodical observation.", None),
    (StopTitlebarObservation,"StopTitlebarObservation","Stop periodical observation","Stops periodical observation.", None),
    (ShowMenu,"ShowMenu","ShowMenu","ShowMenu.",None),
    (eg.ActionGroup, 'Window', 'Window', 'Window',(
        (SendMessageActions,"Minimize","Minimize window","Minimize window.",2),
        (WindowControl,"Restore","Restore window","Restore window.",SC_RESTORE),
#        (SendMessageActions,"MinimRest","Minimize/Restore","Minimize/Restore window.",1075),
        (MinimRest,"MinimRest","Minimize/Restore","Minimize/Restore window.",None),
        (SendMessageActions,"Expand","Collapse/Expand window","Collapse/Expand window.",1076),
        (SendMessageActions,"OnTop","Stay on top On/Off","Stay on top On/Off.",1077),
    )),
    (eg.ActionGroup, 'MainControl', 'Main control', 'Main control',(
        (SendMessageActions,"PlayStop","Play/Stop","Play/Stop.",1000),
        (CheckAndChange,"Play","Play","Play.",(0, False, 1000)),
        (SendMessageActions,"Stop","Stop","Stop.",1008),
        (GetStatus,"GetPlaying","Get status of playing","Get status of playing.",0),
        (SendMessageActions,"MuteOnOff","Mute On/Off","Mute On/Off.",1027),
        (CheckAndChange,"MuteOn","Mute on","Mute on.",(1, False, 1027)),
        (CheckAndChange,"MuteOff","Mute off","Mute off.",(1, True, 1027)),
        (GetStatus,"GetMuted","Get muted","Get muted.",1),
        (SendMessageActions,"RecOnOff","Recording On/Off","Recording On/Off.",1051),
        (CheckAndChange,"RecOn","Recording on","Recording on.",(2, False, 1051)),
        (CheckAndChange,"RecOff","Recording off","Recording off.",(2, True, 1051)),
        (GetStatus,"GetRecording","Get recording","Get recording.",2),
        (SendMessageActions,"RecOnlyCurr",'Toggle "Record only current track"','Toggle "Record only current track".', 4036),
        (CheckAndChange,"RecOnlyOn",'Set "Record only current track"','Set "Record only current track".',(3, False, 4036)),
        (CheckAndChange,"RecOnlyOff",'Clear "Record only current track"','Clear "Record only current track".',(3, True, 4036)),
        (GetStatus,"GetRecOnlyCurr",'Get "Record only current track"','Get "Record only current track".',3),
    )),
    (eg.ActionGroup, 'Volume', 'Volume', 'Volume',(
        (GetVolume,"GetVolume","Get volume","Get volume.", None),
        (SetVolume,"SetVolume","Set volume","Set volume.", 0),
        (SetVolume,"VolumeUp","Volume up","Volume up.", 1),
        (SetVolume,"VolumeDown","Volume down","Volume down.", 2),
    )),
    (eg.ActionGroup, 'Clipboard', 'Clipboard', 'Clipboard',(
        (SendMessageActions,"CopyURLtoClipboard","Copy URL to Clipboard","Copy URL to Clipboard.", 4037),
        (SendMessageActions,"CopyTitleToClipboard","Copy Title to Clipboard","Copy Title to Clipboard.", 4038),
        (SendMessageActions,"PlayURLfromClipboard","Play URL from Clipboard","Play URL from Clipboard.", 4039),
    )),
    (eg.ActionGroup, 'Equalizer', 'Equalizer', 'Equalizer',(
        (SendMessageActions,"EqualizerOff","Equalizer Off","Equalizer Off.", 4040),
        (SendMessageActions,"EqualizerJazz","Equalizer Jazz","Equalizer Jazz.", 4041),
        (SendMessageActions,"EqualizerPop","Equalizer Pop","Equalizer Pop.", 4042),
        (SendMessageActions,"EqualizerRock","Equalizer Rock","Equalizer Rock.", 4043),
        (SendMessageActions,"EqualizerClassic","Equalizer Classic","Equalizer Classic.", 4044),
        (GetMenuItem, "GetEqualizerIndex", "Get Equalizer", "Get Equalizer.", 9),
    )),
    (eg.ActionGroup, 'SleepTimer', 'Sleep timer', 'Sleep timer',(
        (SendMessageActions,"SleepTimerOff","Sleep timer Off","Sleep timer Off.", 4034),
        (SendMessageActions,"SleepIn5Min","Sleep in 5 min","Sleep in 5 min.", 4026),
        (SendMessageActions,"SleepIn10Min","Sleep in 10 min","Sleep in 10 min.", 4027),
        (SendMessageActions,"SleepIn15Min","Sleep in 15 min","Sleep in 15 min.", 4028),
        (SendMessageActions,"SleepIn20Min","Sleep in 20 min","Sleep in 20 min.", 4029),
        (SendMessageActions,"SleepIn30Min","Sleep in 30 min","Sleep in 30 min.", 4030),
        (SendMessageActions,"SleepIn60Min","Sleep in 60 min","Sleep in 60 min.", 4031),
        (SendMessageActions,"SleepIn90Min","Sleep in 90 min","Sleep in 90 min.", 4032),
        (SendMessageActions,"SleepIn120Min","Sleep in 120 min","Sleep in 120 min.", 4033),
    )),
    (eg.ActionGroup, 'Fav_and_Hist', 'Favorites and History', 'Favorites and History',(
        (SendMessageActions,"AddFav","Add to favorites","Add current station to favorites.",1324),
        (SendMessageActions,"RemFav","Remove from favorites","Remove current station from favorites.",1325),
        (SelectFav,"SelectFav","Select favorite (preset number)","Select favorite by preset number (order).", None),
        (NextPrevFav,"NextFav","Next favorite","Next favorite.", 1),
        (NextPrevFav,"PreviousFav","Previous favorite","Previous favorite.", -1),
        (RandomFav,"RandomFav","Random favorite","Random favorite.", None),
        (SendMessageActions,"PreviousHist","Back in history","Back in history.",1038),
        (SendMessageActions,"ForwardHist","Forward in history","Forward in history.",1039),
        (OpenManager,"OpenManager","Open manager","Open manager.", None),
        (HideManager,"HideManager","Hide manager","Hide manager.", None),
        (GetFavorites,"GetFavorites","Get favorites","Get favorites.", None),
        (GetMenuItem, "GetFavoritesIndex", "Get last played favorite station", "Get last played favorite station.", 7),
    )),
    (eg.ActionGroup, 'Scheduler', 'Scheduler', 'Scheduler',(
        (OpenScheduler,"OpenScheduler","Open scheduler","Open scheduler.", None),
        (HideScheduler,"HideScheduler","Hide scheduler","Hide scheduler.", None),
        (EnableSchedule,"EnableSchedule","Enable schedule","Enable schedule.", 1),
        (EnableSchedule,"DisableSchedule","Disable schedule","Disable schedule.", 0),
        (EnableAll,"EnableAll","Enable all schedules","Enable all schedules.", 1),
        (EnableAll,"DisableAll","Disable all schedules","Disable all schedules.", 0),
        (EnableSchedule,"GetSchedule","Get schedule","Get schedule.", -1),
        (AddSchedule,"AddSchedule","Add schedule",AddSchedule.text.descr, None),
        (DeleteSchedule,"DeleteSchedule","Delete schedule","Delete schedule.", None),
        (RunScheduleImmediately, "RunScheduleImmediately", "Run schedule immediately", "Runs schedule immediately.", None),
    )),
)
#===============================================================================