# -*- coding: utf-8 -*-

version="0.1.7" 

# Copyright (C)  2008-2011 Pako  (lubos.ruckl@quick.cz)
# 
# This file is a plugin for EventGhost.
# Copyright (C) 2005-2009 Lars-Peter Voss <bitmonster@eventghost.org>
#
# EventGhost is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License version 2 as published by the
# Free Software Foundation;
#
# 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 this program. If not, see <http://www.gnu.org/licenses/>.
#
# Changelog (in reverse chronological order):
# -------------------------------------------
# 0.1.7  by Pako 2011-04-11 08:33 UTC+1
#        - added eg.ParseString for some inputs
# 0.1.6  by Pako 2010-04-15 15:27 GMT+1
#===============================================================================

eg.RegisterPlugin(
    name = "File Operations",
    author = "Pako",
    version = version,
    kind = "other",
    guid = "{50D933C5-F93B-4A8A-A6CE-95A40F906036}",
    createMacrosOnAdd = False,
    icon = (
        "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAA"
        "ABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAINSURBVBgZBcG/r55z"
        "GAfg6/4+z3va01NHlYgzEfE7MdCIGISFgS4Gk8ViYyM2Mdlsko4GSf8Do0FLRCIkghhY"
        "JA3aVBtEz3nP89wf11VJvPDepdd390+8Nso5nESBQoq0pfvXm9fzWf19453LF85vASqJ"
        "lz748vInb517dIw6EyYBIIG49u+xi9/c9MdvR//99MPPZ7+4cP4IZhhTPbwzT2d+vGoa"
        "VRRp1rRliVvHq+cfvM3TD82+7mun0o/ceO7NT+/4/KOXjwZU1ekk0840bAZzMQ2mooqh"
        "0A72d5x/6sB9D5zYnff3PoYBoWBgFKPKqDKqjCpjKr//dcu9p489dra88cydps30KswA"
        "CfNEKanSaxhlntjJ8Mv12Paie+vZ+0+oeSwwQ0Iw1xAR1CiFNJkGO4wu3ZMY1AAzBI0q"
        "SgmCNJsJUEOtJSMaCTBDLyQ0CknAGOgyTyFFiLI2awMzdEcSQgSAAKVUmAeNkxvWJWCG"
        "tVlDmgYQ0GFtgg4pNtOwbBcwQy/Rife/2yrRRVI0qYCEBly8Z+P4qMEMy7JaVw72N568"
        "e+iwhrXoECQkfH91kY7jwwXMsBx1L93ZruqrK6uuiAIdSnTIKKPLPFcvay8ww/Hh+ufe"
        "znTXu49v95IMoQG3784gYXdTqvRmqn/Wpa/ADFX58MW3L71SVU9ETgEIQQQIOOzub+fh"
        "IvwPRDgeVjWDahIAAAAASUVORK5CYII="
    ),
    description = (
        "File Operations (reading, periodical reading and writing)."
    ),
    url = "http://www.eventghost.net/forum/viewtopic.php?t=1011"
)
#===============================================================================

import os
import time
import codecs
from threading import Thread, Event

def String2Hex(strng, length = '2'):
    tmp = []
    s2h = "%0" + length + "X "
    for c in strng:
        tmp.append( s2h % ord( c ) )    
    return ''.join( tmp ).strip()  
#===============================================================================

class ObservationThread(Thread):
    def __init__(
        self,
        stp,
    ):
        self.abort = False
        self.aborted = False
        self.lastCheck = 0
        self.threadFlag = Event()
        #self.firstRun = True
        
        self.inCoding = stp[0]
        self.fileName = eg.ParseString(stp[1])
        self.mode = stp[2]
        self.errDecMode = stp[3]
        self.inPage = stp[4]
        self.fromLine = stp[5]
        self.direction = stp[6]
        self.lines = stp[7]
        self.period = stp[8]
        self.evtName = eg.ParseString(stp[9])
        self.trigger = stp[10]
        self.oldData = None
        Thread.__init__(self, name = self.evtName.encode('unicode_escape')+'_Thread')

    def run(self):
        while 1:
            errorList = ('strict','ignore','replace')
            try:
                input = codecs.open(self.fileName,'r',self.inPage, errorList[self.errDecMode])
            except:
                raise  
            else:
                if self.lines > 0:
                    data = input.readlines()
                    if self.direction == 0: #from beginning
                        data = data[self.fromLine-1:self.fromLine+self.lines-1]
                    else:              #from end
                        if self.fromLine-self.lines < 1:
                            data = data[-self.fromLine:]
                        else:
                            data = data[-self.fromLine:-(self.fromLine-self.lines)]
                    if self.mode == 2:      #one string
                        data = ''.join(data)
                    elif self.mode == 0:    #without CR/LF
                        tmp = []
                        for line in data:
                            tmp.append(line.rstrip())
                        data = tmp
                    if self.lines == 1:
                        if len(data) > 0: #empty file ?
                            data = data[0]
                        else:
                            data = ''
                else:                  #whole file
                    data = input.read()
                try:    
                    input.close()
                except:
                    raise
                flag = True
                while True:
                    if self.trigger == 0:   #always
                        break
                    elif self.trigger == 1: #always if not empty
                        if self.mode == 2:
                            if data != '':
                                break
                        else:
                            if data != []:
                                break
                    elif self.trigger == 2: #only at change
                        if data != self.oldData:
                            break
                    else:                   #only at change and not empty
                        if data != self.oldData:
                            if self.mode == 2:
                                if data != '':
                                    break
                            else:
                                if data != []:
                                    break
                    flag = False
                    break
                if flag:
                    eg.TriggerEvent(self.evtName, payload = data, prefix = 'File')
                self.oldData = data
  
            if self.abort:
                break
            self.lastCheck = time.time()
            self.threadFlag.wait(self.period)
            self.threadFlag.clear()
        self.aborted = True


    def AbortObservation(self, close=False):
        self.abort = True
        self.threadFlag.set()
#===============================================================================

class FileOperations(eg.PluginClass):
    def __init__(self):
        self.AddAction(Read)
        self.AddAction(ReadPeriodically)
        self.AddAction(AbortPeriodicalRead)
        self.AddAction(AbortAllPeriodicalRead)
        self.AddAction(Write)
        self.observThreads = {}
        self.observData = []


    def StartObservation(
        self,
        stp,
    ):
        observName = eg.ParseString(stp[9])
        if observName in self.observThreads:
            ot = self.observThreads[observName]
            if ot.isAlive():
                ot.AbortObservation()
            del self.observThreads[observName]
        ot = ObservationThread(
            stp,
        )
        ot.start()
        self.observThreads[observName] = ot

    def AbortObservation(self, observName):
        if observName in self.observThreads:
            ot = self.observThreads[observName]
            ot.AbortObservation()

    def AbortAllObservations(self, close=False):
        thrds = list(enumerate(self.observThreads))
        thrds.reverse()
        for i, item in thrds:
            ot = self.observThreads[item]
            ot.AbortObservation(close)


    def Configure(self, *args):
        panel = eg.ConfigPanel(self, resizable=True)

        panel.sizer.Add(
            wx.StaticText(panel, -1, self.text.header),
            flag = wx.ALIGN_CENTER_VERTICAL
        )

        mySizer = wx.GridBagSizer(5, 5)
        mySizer.AddGrowableRow(0)
        mySizer.AddGrowableCol(2)
       
        observListCtrl = wx.ListCtrl(panel, -1, style=wx.LC_REPORT | wx.VSCROLL | wx.HSCROLL)
       
        for i, colLabel in enumerate(self.text.colLabels):
            observListCtrl.InsertColumn(i, colLabel)

        #setting cols width
        observListCtrl.InsertStringItem(0, 30*"X")
        observListCtrl.SetStringItem(0, 1, 16*"X")
        observListCtrl.SetStringItem(0, 2, 16*"X")

        size = 0
        for i in range(3):
            observListCtrl.SetColumnWidth(i, wx.LIST_AUTOSIZE_USEHEADER)
            size += observListCtrl.GetColumnWidth(i)
       
        observListCtrl.SetMinSize((size, -1))
       
        mySizer.Add(observListCtrl, (0,0), (1, 4), flag = wx.EXPAND)

        #buttons
        abortButton = wx.Button(panel, -1, "Abort")
        mySizer.Add(abortButton, (1,0))
       
        abortAllButton = wx.Button(panel, -1, "Abort all")
        mySizer.Add(abortAllButton, (1,1), flag = wx.ALIGN_CENTER_HORIZONTAL)
              
        refreshButton = wx.Button(panel, -1, "Refresh")
        mySizer.Add(refreshButton, (1,3), flag = wx.ALIGN_RIGHT)
       
        panel.sizer.Add(mySizer, 1, flag = wx.EXPAND)
       
        def PopulateList (event=None):
            observListCtrl.DeleteAllItems()
            row = 0
            for i, item in enumerate(self.observThreads):
                t = self.observThreads[item]
                if t.isAlive():
                    observListCtrl.InsertStringItem(row, os.path.split(t.fileName)[1])
                    observListCtrl.SetStringItem(row, 1, t.evtName)
                    observListCtrl.SetStringItem(row, 2, str(t.period) + " sec")
                    row += 1
            ListSelection(wx.CommandEvent())

        def OnAbortButton(event):
            item = observListCtrl.GetFirstSelected()
            while item != -1:
                cell = observListCtrl.GetItem(item,1)
                evtName = cell.GetText()
                ot = self.observThreads[evtName]
                self.AbortObservation(evtName)
                while not ot.aborted:
                    pass
                item = observListCtrl.GetNextSelected(item)
            PopulateList()
            event.Skip()

        def OnAbortAllButton(event):
            self.AbortAllObservations()
            PopulateList()
            event.Skip()

        def ListSelection(event):
            flag = observListCtrl.GetFirstSelected() != -1
            abortButton.Enable(flag)
            event.Skip()
           
        def OnSize(event):
            observListCtrl.SetColumnWidth(6, wx.LIST_AUTOSIZE_USEHEADER)
            event.Skip()

        PopulateList()
       
        abortButton.Bind(wx.EVT_BUTTON, OnAbortButton)
        abortAllButton.Bind(wx.EVT_BUTTON, OnAbortAllButton)
        refreshButton.Bind(wx.EVT_BUTTON, PopulateList)
        observListCtrl.Bind(wx.EVT_LIST_ITEM_SELECTED, ListSelection)
        observListCtrl.Bind(wx.EVT_LIST_ITEM_DESELECTED, ListSelection)
        panel.Bind(wx.EVT_SIZE, OnSize)

        while panel.Affirmed():
            panel.SetResult(*args)
            
    #function to fill the action's Comboboxes
    def GetObservData(self):
        self.observData.sort(lambda a,b: cmp(a[1].lower(), b[1].lower()))
        return self.observData

    #function to collect data for action's Comboboxes
    def AddObservData(self, stp):
        item = (os.path.split(stp[1])[1],stp[9])
        if not item in self.observData:
            self.observData.append(item)

    class text:
        FilePath = "Read file:"
        browseFileDialogTitle = "Choose the file"
        txtMode = "Line(s) return like as a:"
        listNotIncluding = "List of line strings without CR/LF"
        listIncluding = "List of line strings including CR/LF"
        oneNotIncluding = "String without CR/LF"
        oneIncluding = "String including CR/LF"
        oneString = "One string (including CR/LF)"
        systemPage = "system code page (%s)"
        defaultIn = "unicode (UTF-8)"
        inputPage = "Input data coding:"
        txtDecErrMode = "Error handling during decoding:"
        strict = "Raise an exception"
        ignore = "Ignore (skip bad chars)"
        replace = "Replace bad chars"
        lineNum = "Start read at line number:"
        begin = "from the beginning"
        end = "from the end"
        readAhead = "Read"
        readBehind = "lines (0 = whole file)"
        intervalLabel = "Refresh interval (s):"
        evtNameLabel = "Observation and event name:"
        triggerLabel = "Event trigger:"
        triggerChoice = (
            "always",
            "always if not empty",
            "only at changes",
            "only at changes and if not empty"
        )        
        header = "Currently active file observations:"
        colLabels = (
            "File",
            "Event name",
            "Interval")            
#===============================================================================

class Read(eg.ActionClass):
    name = "Read text from file"
    description = "Reads text from selected file."
    
    def __call__(
        self,
        inCoding = 0,
        fileName = '',
        mode = 0,
        errDecMode = 0,
        inPage = "",
        fromLine = 1,
        direction = 0,
        lines = 1,
    ):
        fileName = eg.ParseString(fileName)
        errorList = ('strict', 'ignore', 'replace')
        try:
            input = codecs.open(fileName, 'r', inPage, errorList[errDecMode])
        except:
            raise
        else:
            data = input.readlines()
            if lines == 0:
                direction = 0
                lines = len(data)
                fromLine = 1
            if direction == 0: #from beginning
                data = data[fromLine-1:fromLine+lines-1]
            else:              #from end
                if fromLine-lines < 1:
                    data = data[-fromLine:]
                else:
                    data = data[-fromLine:-(fromLine-lines)]
            if mode == 2:      #one string
                data = ''.join(data)
            elif mode == 0:    #without CR/LF
                tmp = []
                for line in data:
                    tmp.append(line.rstrip())
                data = tmp
            if lines == 1:
                if len(data) > 0: #empty file ?
                    data = data[0]
                else:
                    data = ''
            try:    
                input.close()
            except:
                raise                
            return data
            
    def GetLabel(
        self,
        inCoding,
        fileName,
        mode,
        errDecMode,
        inPage,
        fromLine,
        direction,
        lines = 1,
    ):
        return '%s: %s' % (str(self.name), os.path.split(fileName)[1])

    def Configure(
        self,
        inCoding = 0,
        fileName = '',
        mode = 0,
        errDecMode = 0,
        inPage="",
        fromLine=1,
        direction = 0,
        lines = 1,
    ):
        from codecsList import codecsList
        panel = eg.ConfigPanel(self)
        text = self.plugin.text
        self.mode = mode
    #Controls    
        inPageText = wx.StaticText(panel, -1, text.inputPage)
        labelMode = wx.StaticText(panel, -1, text.txtMode)
        labelDecErrMode = wx.StaticText(panel, -1, text.txtDecErrMode)
        fileText = wx.StaticText(panel, -1, text.FilePath)
        filepathCtrl = eg.FileBrowseButton(
            panel, 
            -1, 
            initialValue=fileName,
            labelText="",
            fileMask="*.*",
            buttonText=eg.text.General.browse,
            dialogTitle=text.browseFileDialogTitle
        )
        width = labelDecErrMode.GetTextExtent(text.txtDecErrMode)[0]
        choiceDecErrMode = wx.Choice(
            panel,
            -1,
            size = ((width,-1)),
            choices=(text.strict, text.ignore, text.replace)
        )
        choiceDecErrMode.SetSelection(errDecMode)
        choices = [text.systemPage % eg.systemEncoding, text.defaultIn]
        choices.extend(codecsList)
        inPageCtrl = wx.Choice(panel,-1,choices=choices)
        inPageCtrl.SetSelection(inCoding)
        lineNumLbl=wx.StaticText(panel, -1, text.lineNum)
        fromLineNumCtrl = eg.SpinIntCtrl(
            panel,
            -1,
            fromLine,
            min = 1,
            max = 999,
        )
        rb0 = panel.RadioButton(not direction, text.begin, style=wx.RB_GROUP)
        rb1 = panel.RadioButton(direction, text.end)                            
        lblAhead = wx.StaticText(panel, -1, text.readAhead)
        lblBehind = wx.StaticText(panel, -1, text.readBehind)
        linesNumCtrl = eg.SpinIntCtrl(
            panel,
            -1,
            lines,
            min = 0,
            max = 999,
        )
        w0 = inPageCtrl.GetTextExtent(text.listNotIncluding)[0]
        w1 = inPageCtrl.GetTextExtent(text.listIncluding)[0]
        w2 = inPageCtrl.GetTextExtent(text.oneNotIncluding)[0]
        w3 = inPageCtrl.GetTextExtent(text.oneIncluding)[0]
        w4 = inPageCtrl.GetTextExtent(text.oneString)[0]
        width = max(w0,w1,w2,w3,w4)+30
        choiceMode = wx.Choice(panel,-1,size=(width,-1))
    #Sizers
        topSizer = wx.FlexGridSizer(2,0,2,15)
        topSizer.AddGrowableCol(0,1)
        topSizer.AddGrowableCol(1,1)
        topSizer.Add(inPageText,0,wx.EXPAND)
        topSizer.Add(labelDecErrMode,0,wx.EXPAND)
        topSizer.Add(inPageCtrl,0,wx.EXPAND)
        topSizer.Add(choiceDecErrMode,0,wx.EXPAND)
        fromSizer = wx.BoxSizer(wx.HORIZONTAL)
        fromSizer.Add(lineNumLbl,0,wx.TOP,4)
        fromSizer.Add(fromLineNumCtrl,0,wx.LEFT,10)
        fromSizer.Add(rb0,0,wx.EXPAND|wx.LEFT,20)
        fromSizer.Add(rb1,0,wx.EXPAND|wx.LEFT,15)
        linesSizer = wx.BoxSizer(wx.HORIZONTAL)
        linesSizer.Add(lblAhead,0,wx.TOP,4)
        linesSizer.Add(linesNumCtrl,0,wx.LEFT|wx.RIGHT,8)
        linesSizer.Add(lblBehind,0,wx.TOP,4)
        mainSizer = wx.BoxSizer(wx.VERTICAL)
        mainSizer.Add(fileText,0,wx.EXPAND)
        mainSizer.Add(filepathCtrl,0,wx.EXPAND)
        mainSizer.Add(topSizer,0,wx.TOP|wx.EXPAND,5)
        mainSizer.Add(linesSizer,0,wx.TOP|wx.EXPAND,11)        
        mainSizer.Add(fromSizer,0,wx.TOP|wx.EXPAND,11)        
        mainSizer.Add(labelMode,0,wx.TOP|wx.EXPAND,11)
        mainSizer.Add(choiceMode,0,wx.TOP,2)
        panel.sizer.Add(mainSizer,0,wx.EXPAND)
        
        def onLinesNumCtrl(event=None):
            flag = False
            if event:
                self.mode = choiceMode.GetSelection()
            if linesNumCtrl.GetValue() == 0:
                fromLineNumCtrl.SetValue(1)
                rb0.SetValue(True)
                rb1.SetValue(False)
                lineNumLbl.Enable(False)
                fromLineNumCtrl.Enable(False)
                rb0.Enable(False)
                rb1.Enable(False)
            else:
                lineNumLbl.Enable(True)                
                fromLineNumCtrl.Enable(True)
                rb0.Enable(True)
                rb1.Enable(True)
            
            if linesNumCtrl.GetValue() == 1:
                choiceMode.Clear()
                choiceMode.AppendItems(strings=(text.oneNotIncluding,text.oneIncluding))
            else:
                if len(choiceMode.GetStrings()) != 3:
                    choiceMode.Clear()
                    choiceMode.AppendItems(
                        strings=(text.listNotIncluding,text.listIncluding,text.oneString)
                    )    
                    if self.mode == 2:
                        flag = True
            if event:
                choiceMode.SetSelection(0)
                event.Skip()
                if flag:
                    self.mode = 0
            choiceMode.SetSelection(self.mode)
        linesNumCtrl.Bind(wx.EVT_SPIN, onLinesNumCtrl)
        onLinesNumCtrl()
            
        while panel.Affirmed():
            inCoding = inPageCtrl.GetSelection()
            pgTpl = (eg.systemEncoding, 'utf8')
            panel.SetResult(
                inCoding,
                filepathCtrl.GetValue(),
                choiceMode.GetSelection(),
                choiceDecErrMode.GetSelection(),
                inPageCtrl.GetStringSelection() if inCoding > 1 else pgTpl[inCoding],
                fromLineNumCtrl.GetValue(),
                rb1.GetValue(),
                linesNumCtrl.GetValue(),
            )
#===============================================================================

class AbortPeriodicalRead(eg.ActionClass):
    name = "Abort periodical reading"
    description = "Aborts periodical reading of text from selected file."
        
    def __call__(self, observName='', file = ''):
        observName = eg.ParseString(observName)
        self.plugin.AbortObservation(observName)
    
    def GetLabel(self, observName, file):
        return '%s: %s -> %s' % (str(self.name), file, observName)

    def Configure(self, observName='', file = ''):
        text=self.text
        panel = eg.ConfigPanel(self)
        choices = [item[1] for item in self.plugin.GetObservData()]
        fileLbl = wx.StaticText(panel, -1, '')
        fileLbl.Enable(False)
        nameLbl=wx.StaticText(panel, -1, text.nameObs)
        nameCtrl=wx.ComboBox(panel,-1,choices = choices)
        nameCtrl.SetStringSelection(observName)
        mainSizer = wx.BoxSizer(wx.VERTICAL)
        mainSizer.Add(fileLbl,0)
        mainSizer.Add((1,20))
        mainSizer.Add(nameLbl,0)
        mainSizer.Add(nameCtrl,0,wx.EXPAND)
        panel.sizer.Add(mainSizer)
        panel.sizer.Layout()
        
        def onComboBox(event = None):
            choices = [item[1] for item in self.plugin.GetObservData()]
            evtName = nameCtrl.GetValue()
            if evtName in choices:
                indx = choices.index(evtName)
                fileName = self.plugin.GetObservData()[indx][0]
                lbl = text.fileLabel  % fileName
            else:
                lbl = ''
            fileLbl.SetLabel(lbl)
            if event:
                event.Skip()
        onComboBox()
        nameCtrl.Bind(wx.EVT_COMBOBOX,onComboBox)
        
        # re-assign the test button
        def OnTestButton(event):
            self.plugin.AbortObservation(nameCtrl.GetValue())
        panel.dialog.buttonRow.testButton.SetLabel(text.abortNow)
        panel.dialog.buttonRow.testButton.SetToolTipString(text.tip)
        panel.dialog.buttonRow.testButton.Bind(wx.EVT_BUTTON, OnTestButton)
        
        while panel.Affirmed():
            lbl = fileLbl.GetLabel()
            if lbl != '':
                fileName = lbl[2+lbl.rfind(':'):]
            else:
                fileName = ''
            panel.SetResult(
                nameCtrl.GetValue(),
                fileName
            )
    class text:
        nameObs = 'Observation and event name:'
        abortNow = 'Abort now !'
        tip = 'Abort observation now'
        fileLabel = 'File to read: %s'
#===============================================================================
    
class AbortAllPeriodicalRead(eg.ActionClass):
    name = "Abort all periodical reading"
    description = "Aborts all periodical reading of text from file."
    def __call__(self):
        self.plugin.AbortAllObservations()
#===============================================================================

class ReadPeriodically(eg.ActionClass):
    name = "Start periodical reading"
    description = ("Starts periodical reading of text from selected file. "
                   "Learning the line(s) return as payload of event.")
    
    def startObserv(self, stp):
        self.plugin.StartObservation(stp)
        
    def __call__(self, stp):
        self.startObserv(stp)            

    def GetLabel(
        self,
        stp
    ):
        self.plugin.AddObservData(stp)
        return '%s: %s -> %s' % (str(self.name), os.path.split(stp[1])[1], stp[9])
        
    def Configure(
        self,
        stp = []
    ):
        if stp == []:
            inCoding = 0
            fileName = ''
            mode = 0
            errDecMode = 0
            inPage = ""
            fromLine = 1
            direction = 0
            lines = 1
#            period = 0.1
            period = 1
            evtName = ''
            trigger = 1
        else:
            inCoding = stp[0]
            fileName = stp[1]
            mode = stp[2]
            errDecMode = stp[3]
            inPage = stp[4]
            fromLine = stp[5]
            direction = stp[6]
            lines = stp[7]
            period = stp[8]
            evtName = stp[9]
            trigger = stp[10]
        
        from codecsList import codecsList
        panel = eg.ConfigPanel(self)
        text = self.plugin.text
        self.mode = mode
    #Controls    
        inPageText = wx.StaticText(panel, -1, text.inputPage)
        labelMode = wx.StaticText(panel, -1, text.txtMode)
        labelDecErrMode = wx.StaticText(panel, -1, text.txtDecErrMode)
        fileText = wx.StaticText(panel, -1, text.FilePath)
        filepathCtrl = eg.FileBrowseButton(
            panel, 
            -1, 
            initialValue=fileName,
            labelText="",
            fileMask="*.*",
            buttonText=eg.text.General.browse,
            dialogTitle=text.browseFileDialogTitle
        )
        width = labelDecErrMode.GetTextExtent(text.txtDecErrMode)[0]
        choiceDecErrMode = wx.Choice(
            panel,
            -1,
            size = ((width,-1)),
            choices=(text.strict, text.ignore, text.replace)
        )
        choiceDecErrMode.SetSelection(errDecMode)
        choices = [text.systemPage % eg.systemEncoding, text.defaultIn]
        choices.extend(codecsList)
        inPageCtrl = wx.Choice(panel,-1,choices=choices)
        inPageCtrl.SetSelection(inCoding)
        lineNumLbl=wx.StaticText(panel, -1, text.lineNum)
        fromLineNumCtrl = eg.SpinIntCtrl(
            panel,
            -1,
            fromLine,
            min = 1,
            max = 999,
        )        
        rb0 = panel.RadioButton(not direction, text.begin, style=wx.RB_GROUP)
        rb1 = panel.RadioButton(direction, text.end)                            
        lblAhead = wx.StaticText(panel, -1, text.readAhead)
        lblBehind = wx.StaticText(panel, -1, text.readBehind)
        linesNumCtrl = eg.SpinIntCtrl(
            panel,
            -1,
            lines,
            min = 0,
            max = 999,
        )
        periodNumCtrl = eg.SpinNumCtrl(
            panel,
            -1,
            period,
            integerWidth = 5,
            fractionWidth = 1,
            allowNegative = False,
            min = 0.1,
            increment = 0.1,
        )
        intervalLbl = wx.StaticText(panel, -1, text.intervalLabel)
        w0 = inPageCtrl.GetTextExtent(text.listNotIncluding)[0]
        w1 = inPageCtrl.GetTextExtent(text.listIncluding)[0]
        w2 = inPageCtrl.GetTextExtent(text.oneNotIncluding)[0]
        w3 = inPageCtrl.GetTextExtent(text.oneIncluding)[0]
        w4 = inPageCtrl.GetTextExtent(text.oneString)[0]
        width = max(w0,w1,w2,w3,w4)+30
        choiceMode = wx.Choice(panel,-1,size=(width,-1))
        evtNameCtrl = wx.TextCtrl(panel,-1,evtName)
        evtNameLbl = wx.StaticText(panel, -1, text.evtNameLabel)
        triggerCtrl = wx.Choice(panel,-1, choices = text.triggerChoice)
        triggerCtrl.SetSelection(trigger)
        triggerLbl = wx.StaticText(panel, -1, text.triggerLabel)
    #Sizers
        topSizer = wx.FlexGridSizer(2,0,2,25)
        topSizer.AddGrowableCol(0,1)
        topSizer.AddGrowableCol(1,1)
        topSizer.Add(inPageText,0,wx.EXPAND)
        topSizer.Add(labelDecErrMode,0,wx.EXPAND)
        topSizer.Add(inPageCtrl,0,wx.EXPAND)
        topSizer.Add(choiceDecErrMode,0,wx.EXPAND)
        fromSizer = wx.BoxSizer(wx.HORIZONTAL)
        fromSizer.Add(lineNumLbl,0,wx.TOP,4)
        fromSizer.Add(fromLineNumCtrl,0,wx.LEFT,10)
        fromSizer.Add(rb0,0,wx.EXPAND|wx.LEFT,20)
        fromSizer.Add(rb1,0,wx.EXPAND|wx.LEFT,15)
        linesSizer = wx.BoxSizer(wx.HORIZONTAL)
        linesSizer.Add(lblAhead,0, flag = wx.TOP, border = 4)
        linesSizer.Add(linesNumCtrl,0,wx.LEFT|wx.RIGHT,8)
        linesSizer.Add(lblBehind,0, flag = wx.TOP, border = 4)
        periodSizer = wx.BoxSizer(wx.HORIZONTAL)
        periodSizer.Add(intervalLbl,0, wx.TOP|wx.RIGHT, 4)
        periodSizer.Add(periodNumCtrl,0, wx.RIGHT)
        
        mainSizer = wx.BoxSizer(wx.VERTICAL)
        mainSizer.Add(fileText,0,wx.EXPAND)
        mainSizer.Add(filepathCtrl,0,wx.EXPAND)
        mainSizer.Add(topSizer,0,wx.TOP|wx.EXPAND,5)
        mainSizer.Add(linesSizer,0,wx.TOP|wx.EXPAND,11)        
        mainSizer.Add(fromSizer,0,wx.TOP|wx.EXPAND,11)
        bottomSizer = wx.FlexGridSizer(4,0,2,25)       
        bottomSizer.AddGrowableCol(0,1)
        bottomSizer.AddGrowableCol(1,1)
        bottomSizer.Add(labelMode,0,wx.EXPAND)
        bottomSizer.Add((1,1))
        bottomSizer.Add(choiceMode,0,wx.EXPAND)
        bottomSizer.Add(periodSizer,0,wx.EXPAND|wx.RIGHT,3)
        bottomSizer.Add(evtNameLbl,0,wx.TOP,8)
        bottomSizer.Add(triggerLbl,0,wx.TOP,8)
        bottomSizer.Add(evtNameCtrl,0,wx.EXPAND)
        bottomSizer.Add(triggerCtrl,0,wx.EXPAND)
        mainSizer.Add(bottomSizer,0,wx.TOP|wx.EXPAND,11)
        panel.sizer.Add(mainSizer,0,wx.EXPAND)
        
        def onLinesNumCtrl(event=None):
            flag = False
            if event:
                self.mode = choiceMode.GetSelection()
            if linesNumCtrl.GetValue() == 0:
                fromLineNumCtrl.SetValue(1)
                rb0.SetValue(True)
                rb1.SetValue(False)
                lineNumLbl.Enable(False)
                fromLineNumCtrl.Enable(False)
                rb0.Enable(False)
                rb1.Enable(False)
            else:
                lineNumLbl.Enable(True)                
                fromLineNumCtrl.Enable(True)
                rb0.Enable(True)
                rb1.Enable(True)
            
            if linesNumCtrl.GetValue() == 1:
                choiceMode.Clear()
                choiceMode.AppendItems(strings=(text.oneNotIncluding,text.oneIncluding))
            else:
                if len(choiceMode.GetStrings()) != 3:
                    choiceMode.Clear()
                    choiceMode.AppendItems(
                        strings=(text.listNotIncluding,text.listIncluding,text.oneString)
                    )    
                    if self.mode == 2:
                        flag = True
            if event:
                choiceMode.SetSelection(0)
                event.Skip()
                if flag:
                    self.mode = 0
            choiceMode.SetSelection(self.mode)
        linesNumCtrl.Bind(wx.EVT_SPIN, onLinesNumCtrl)
        onLinesNumCtrl()
            
        while panel.Affirmed():
            inCoding = inPageCtrl.GetSelection()
            pgTpl = (eg.systemEncoding, 'utf8')
            setup = [
                inCoding,
                filepathCtrl.GetValue(),
                choiceMode.GetSelection(),
                choiceDecErrMode.GetSelection(),
                inPageCtrl.GetStringSelection() if inCoding > 1 else pgTpl[inCoding],
                fromLineNumCtrl.GetValue(),
                rb1.GetValue(),
                linesNumCtrl.GetValue(),
                periodNumCtrl.GetValue(),
                evtNameCtrl.GetValue(),
                triggerCtrl.GetSelection()
            ]
            panel.SetResult(
                setup
            )
#===============================================================================            

class Write(eg.ActionClass):
    name = "Write text to file"
    description = "Writes text to selected file."
    class text:
        TreeLabel = "Write %s to file: %s"
        FilePath = "Output file:"
        browseFileDialogTitle = "Choose the file"
        txtModeMulti = "Mode of write"
        overwrite = "File overwrite"
        append = "Append to file"
        newLine = "Append to file with new line"
        writeToLog = "Write to EventGhost log too"
        systemPage = "system code page (%s)"
        defaultOut = "unicode (UTF-8)"
        hexdump = "String write in the HexDump form"
        inString = "Input text:"
        logTimes = "Write Timestamp"
        outputPage = "Output data coding:"
        txtEncErrMode = "Error handling during encoding:"
        strict = "Raise an exception"
        ignore = "Ignore (skip bad chars)"
        replace = "Replace bad chars"
        internal = 'unicode internal'
    
    def __call__(
        self,
        outCoding,
        string = "",
        fileName = '',
        mode = 0,
        errEncMode = 0,
        log = False,
        times = False,
        hex = False,
        outPage = "",
    ):
        modeStr = 'w' if mode==0 else 'a'
        stamp = time.strftime('%y-%m-%d %H:%M:%S')+'  ' if times else ''
        cr = '\r\n' if mode == 2 else ''        
        errorList = ('strict','ignore','replace')
        string = eg.ParseString(string)
        fileName = eg.ParseString(fileName)
        if hex:
            if outPage != 'unicode_internal':
                string = string.encode(outPage,errorList[errEncMode])
                string = String2Hex(string)
            else:
                string = String2Hex(string,'4')
            outPage = 'ascii'
            
        try:
            file = codecs.open(fileName, modeStr, outPage, errorList[errEncMode]) 
        except:
            raise
        try:
            file.write('%s%s%s' % (stamp, string, cr))
        except:
            raise
        try:    
            file.close()
        except:
            raise
        if log:
            print string
        return string
            
    def GetLabel(
        self,
        outCoding,
        string,
        fileName,
        mode,
        errEncMode,
        log,
        times,
        hex,
        outPage,
    ):
        return self.text.TreeLabel % (string, fileName)

    def Configure(
        self,
        outCoding = 2,
        string = "{eg.result}",
        fileName = u'EG_WTTF.txt',
        mode = 2,
        errEncMode = 0,
        log = False,
        times = False,
        hex = False,
        outPage="",
    ):
        from codecsList import codecsList
        panel = eg.ConfigPanel(self)
        text = self.text
    #Controls    
        stringText = wx.StaticText(panel, -1, text.inString)
        outPageText = wx.StaticText(panel, -1, text.outputPage)
        labelEncErrMode = wx.StaticText(panel, -1, text.txtEncErrMode)
        fileText = wx.StaticText(panel, -1, text.FilePath)
        filepathCtrl = eg.FileBrowseButton(
            panel, 
            -1, 
            initialValue=fileName,
            labelText="",
            fileMask="*.*",
            buttonText=eg.text.General.browse,
            dialogTitle=text.browseFileDialogTitle
        )
        width = labelEncErrMode.GetTextExtent(text.txtEncErrMode)[0]
        choiceEncErrMode = wx.Choice(
            panel,
            -1,
            size = ((width,-1)),
            choices=(text.strict, text.ignore, text.replace)
        )
        stringCtrl = wx.TextCtrl(panel, -1, string, style=wx.TE_NOHIDESEL)
        radioBoxMode = wx.RadioBox(
            panel, 
            -1, 
            text.txtModeMulti,
            choices=[text.overwrite, text.append, text.newLine],
            style=wx.RA_SPECIFY_ROWS
        )
        radioBoxMode.SetSelection(mode)
        choiceEncErrMode.SetSelection(errEncMode)
        writeToLogCheckBox = wx.CheckBox(panel, -1, text.writeToLog)
        writeToLogCheckBox.SetValue(log)
        timesCheckBox = wx.CheckBox(panel, -1, text.logTimes)
        timesCheckBox.SetValue(times)
        hexCheckBox = wx.CheckBox(panel, -1, text.hexdump)
        hexCheckBox.SetValue(hex)
        choices = [text.internal, text.defaultOut, text.systemPage % eg.systemEncoding]
        choices.extend(codecsList)
        outPageCtrl = wx.Choice(panel,-1,choices=choices)
        outPageCtrl.SetSelection(outCoding)
    #Sizers
        topSizer = wx.FlexGridSizer(5,0,1,15)
        topSizer.AddGrowableCol(0,1)
        topSizer.AddGrowableCol(1,1)
        topSizer.Add(stringText,0,wx.EXPAND)
        topSizer.Add(fileText,0,wx.EXPAND)
        topSizer.Add(stringCtrl,0,wx.EXPAND)
        topSizer.Add(filepathCtrl,0,wx.EXPAND)
        topSizer.Add((1,7))
        topSizer.Add((1,7))
        topSizer.Add(outPageText,0,wx.EXPAND)
        topSizer.Add(labelEncErrMode,0,wx.EXPAND)
        topSizer.Add(outPageCtrl,0,wx.EXPAND)
        topSizer.Add(choiceEncErrMode,0,wx.EXPAND)
        chkBoxSizer = wx.BoxSizer(wx.VERTICAL)
        chkBoxSizer.Add(writeToLogCheckBox,0,wx.TOP|wx.LEFT,12)        
        chkBoxSizer.Add(timesCheckBox,0,wx.TOP|wx.LEFT,12)     
        chkBoxSizer.Add(hexCheckBox,0,wx.TOP|wx.LEFT,12)     
        bottomSizer = wx.GridSizer(1,2,1,10)
        bottomSizer.Add(radioBoxMode,0,wx.TOP|wx.EXPAND,5)
        bottomSizer.Add(chkBoxSizer,1,wx.EXPAND)
        mainSizer = wx.BoxSizer(wx.VERTICAL)
        mainSizer.Add(topSizer,0,wx.EXPAND)
        mainSizer.Add(bottomSizer,0,wx.TOP|wx.EXPAND,10)
        panel.sizer.Add(mainSizer,0,wx.EXPAND)

        while panel.Affirmed():
            outCoding = outPageCtrl.GetSelection()
            pgTpl = ('unicode_internal', 'utf8', eg.systemEncoding)
            panel.SetResult(
                outCoding,
                stringCtrl.GetValue(),
                filepathCtrl.GetValue(),
                radioBoxMode.GetSelection(),
                choiceEncErrMode.GetSelection(),
                writeToLogCheckBox.IsChecked(),
                timesCheckBox.IsChecked(),
                hexCheckBox.IsChecked(),
                outPageCtrl.GetStringSelection() if outCoding > 2 else pgTpl[outCoding],
            )        
#===============================================================================
