#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys
import os
import os.path
import time 
import logging
import logging.handlers
import urllib2
import threading
import webbrowser
import wx
import wx.html
from   stomamisc import *

try:
    import wx.aui
    import wx.lib.evtmgr as evtmgr
    import wx.lib.intctrl
except:    
    class PanicApp(wx.App):
        def OnInit(self):
            print '\n< < <  P A N I C  > > >\n'
            print "Can not start StoreMan because the lib 'wx.aui' is missing!"
            print 'Check your installation. The system reports, that you are',
            print WXVERSION, '- but it has to be 2.8 or better!'
            print
            print 'Please install wxPython 2.8 (package python-wxgtk2.8 on'
            print 'Ubuntu / Kubuntu).'
            return True
            
    app = PanicApp(redirect = True)
    app.MainLoop()
   

import stomavlist
import stomacustbar
import stomasystray
import stomaabout
import stomaprefs
import stomajobsman
import stomatasksman
import stomabkupexec
import stomascanman
import stomasqlite
import stomapopup
import stomamountman
import stomatreectrl
import stomadbman
import stomainstall

from configobj import ConfigObj

try:
    from cherrytemplate import renderTemplate
    cherryisthere = True
except:
    cherryisthere = False

           
class StoreManFrame(wx.Frame):
    
    ID_SCANWIN = wx.NewId()
    ID_DBWIN = wx.NewId()
    ID_TASKWIN = wx.NewId()
    ID_JOBWIN = wx.NewId()
    ID_LOGWIN = wx.NewId()
    ID_INI_PERSP = wx.NewId()
    ID_MANAGE_DB = wx.NewId()
    ID_NEWDB = wx.NewId()
    ID_OPENDB = wx.NewId()
    ID_WIZARD = wx.NewId()
    ID_EXAMPLE = wx.NewId()
    ID_MANAGE_JOBS = wx.NewId()     # manage jobs
    ID_MANAGE_TASKS = wx.NewId()    # manage task
    ID_IMMEDIATE_EXEC = wx.NewId()  # immediate job execution
    ID_MOUNTMAN = wx.NewId()
    ID_SELECT_ALL_JOBS = wx.NewId()
    ID_MANAGE_SCAN = wx.NewId()
    ID_EXECUTE_SCAN = wx.NewId()
    ID_ABORT_ACTION = wx.NewId()
    ID_QUERY = wx.NewId()
    ID_REMOVE_ITEM = wx.NewId()
    ID_EXPORT = wx.NewId()
    ID_PRINT = wx.NewId()
    ID_SYSTRAY = wx.NewId()
    ID_EXIT = wx.NewId()
    ID_ABOUT = wx.NewId()
    ID_WEBSITE = wx.NewId()
    ID_UPDATE = wx.NewId()
    ID_INSTALL = wx.NewId()
    ID_DBINFO = wx.NewId()
    ID_HELP = wx.NewId()
    ID_PREFS = wx.NewId()
    ID_CBDIRNAME = wx.NewId()
    ID_CBFILENAME = wx.NewId()
    ID_TCFILESIZE = wx.NewId()
    ID_CBFILEDATE = wx.NewId()
    ID_RADIOBUTTON1 = wx.NewId()
    ID_RADIOBUTTON2 = wx.NewId()
    ID_BUTTON = wx.NewId()
    ID_LISTCTRL = wx.NewId()
    

    def __init__(self, parent, id, title):
        self.handleConfigData('r')

        # make a backup of the ini file
        inifilebak = file(self.inifilename.replace('.ini', '.ini.bak'), 'w')
        self.config.write(inifilebak)
        inifilebak.close()

        windowpos = (wx.Point(int(self.config['Window']['posx']),
                               int(self.config['Window']['posy'])))
        windowsize = (wx.Size(int(self.config['Window']['sizex']),
                              int(self.config['Window']['sizey'])))
        wx.Frame.__init__(self, parent, id, title, pos = windowpos, size = windowsize)  
        self.Bind(wx.EVT_CLOSE, self.onCloseWindow)
        self.Bind(wx.EVT_RIGHT_DOWN, self.onContext)

        wx.InitAllImageHandlers()
        try:   
            self.icon = wx.Icon(ICONPATH + os.sep + APPICON, wx.BITMAP_TYPE_ICO)
            self.SetIcon(self.icon)
        except:
            self.icon = None
        
        self.config['Misc']['mountedbysm'] = []         # temp list

        logfilename = TITLE + '-Action.log'
        logpath = os.path.join(HOMEDIR, logfilename)
        self.logger = logging.getLogger('ActionLog')
        self.logger.setLevel(logging.INFO)
        handler = logging.handlers.RotatingFileHandler(logpath, 'a', ACTION_LOG_SIZE, 4)
        formatter = logging.Formatter("%(asctime)s -- %(message)s", "%Y-%m-%d %H:%M:%S")
        handler.setFormatter(formatter)        
        self.logger.addHandler(handler)
        self.logger.info('%s started - %s' % (logfilename, VERSION))
        
        self.initMenu()                              
        self.mainmenu.Enable(self.ID_INSTALL, False)
        
        self.initToolbar()
        self.updateUnits()

        self.sb = stomacustbar.CustomStatusBar(self)
        self.SetStatusBar(self.sb)

        collist = self.config['Misc']['hlcolor']
        self.hlcolor = (int(collist[0]), int(collist[1]), int(collist[2]))
        
        self._mgr = wx.aui.AuiManager()
        self._mgr.SetManagedWindow(self)

        self.dbwin = wx.ListCtrl(self, -1, size = wx.Size(160, -1),
                                     style = wx.LC_REPORT | wx.LC_NO_HEADER | wx.LC_SINGLE_SEL)
        self.dbwin.InsertColumn(0, '')
        self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.onDbwin, self.dbwin)
        self.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self.onShowDbInfo, self.dbwin) 

        self.scanwin = wx.ListCtrl(self, -1, size = wx.Size(160, -1),
                                   style = wx.LC_REPORT | wx.LC_NO_HEADER | wx.LC_SINGLE_SEL)
        self.scanwin.InsertColumn(0, '')
        self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.onManageScan, self.scanwin)      

        self.jobwin = stomatreectrl.TreeCtrlWithMixin(self, -1, size = wx.Size(150, -1),
                                                      style = wx.TR_LINES_AT_ROOT |
                                                              wx.TR_HAS_BUTTONS |
                                                              wx.TR_HIDE_ROOT |
                                                              wx.TR_FULL_ROW_HIGHLIGHT)
        self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.onEditJobOrTask, self.jobwin)
        self.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK, self.onShowJobOrTaskInfo, self.jobwin)
        self.Bind(wx.EVT_TREE_ITEM_MIDDLE_CLICK, self.onToggleImmediate, self.jobwin)
        self.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.onCollapseOrExpand, self.jobwin)
        self.Bind(wx.EVT_TREE_ITEM_EXPANDED, self.onCollapseOrExpand, self.jobwin)        
        self.jobwin.Bind(wx.EVT_CHAR, self.onChar)
        self.expnodelist = []
        self.updateJobwin(True)
        try:
            explist = [eval(item) for item in  self.config['Window']['tcexpstate']]
        except:
            explist = []
        self.jobwin.SetExpansionState(explist)
        self.onCollapseOrExpand()               # create name expnode list
           
        self.logwin = wx.TextCtrl(self, -1, size = wx.Size(-1, 100), style = wx.TE_RICH | wx.TE_MULTILINE | wx.TE_DONTWRAP | wx.TE_READONLY)
        evtmgr.eventManager.Register(self.onContext, wx.EVT_RIGHT_DOWN, self.logwin)
        
        self.listwin = stomavlist.VirtualListCtrl(self, self.ID_LISTCTRL, self.config)
        self.myfont = wx.Font(int(self.config['ListCtrl']['fsize']), wx.DEFAULT, wx.NORMAL, wx.NORMAL, False, self.config['ListCtrl']['font'])
        self.listwin.SetFont(self.myfont)
        self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.onItemSelected, self.listwin)
        self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.onQueryItemDoubleClick, self.listwin)
        self.Bind(wx.EVT_LIST_COL_CLICK, self.onColumnSort, self.listwin)
        evtmgr.eventManager.Register(self.onContext, wx.EVT_RIGHT_DOWN, self.listwin)

        self._mgr.AddPane(self.dbwin, wx.aui.AuiPaneInfo().Name('histwin').Caption('Databases').Left())
        self._mgr.AddPane(self.scanwin, wx.aui.AuiPaneInfo().Name('scanwin').Caption('Scan dirs').Left())
        self._mgr.AddPane(self.jobwin, wx.aui.AuiPaneInfo().Name('jobwin').Caption('Backup jobs').Left())
        self._mgr.AddPane(self.listwin, wx.aui.AuiPaneInfo().Name('listwin').Caption('Query results').
                          Center().CloseButton(False).MaximizeButton(True))
        self._mgr.AddPane(self.logwin, wx.aui.AuiPaneInfo().Name('logwin').Caption('Log messages').Bottom())
        gradient = wx.aui.AUI_GRADIENT_HORIZONTAL
        self._mgr.GetArtProvider().SetMetric(wx.aui.AUI_DOCKART_GRADIENT_TYPE, gradient)
        flags = self._mgr.GetFlags()
        flags &= ~wx.aui.AUI_MGR_TRANSPARENT_HINT
        flags &= ~wx.aui.AUI_MGR_VENETIAN_BLINDS_HINT
        self._mgr.SetFlags(flags)       
        flag = wx.aui.AUI_MGR_RECTANGLE_HINT
        self._mgr.SetFlags(self._mgr.GetFlags() ^ flag)
        self._mgr.Update()
        self.Bind(wx.aui.EVT_AUI_PANE_CLOSE, self.onPaneClose)
        self.defaultperspective = self._mgr.SavePerspective()
        if len(self.config['Window']['perspective']):
            self._mgr.LoadPerspective(self.config['Window']['perspective'])
         
        self.jobisrunning = False
        self.stomadb = None
        self.dbiswritable = False
        self.connect2Database()
        self.records = None
        self.selitemindex = -1
        
        self.tbicon = stomasystray.SysTrayIcon(self, self.icon, TITLE)          
        self.initContextMenu()
        
        # Printer init
        self.printdata = wx.PrintData()
        self.printdata.SetPaperId(wx.PAPER_LETTER)
        self.printdata.SetPrintMode(wx.PRINT_MODE_PRINTER)
        self.printhtml = wx.html.HtmlEasyPrinting(VERSION, self)

        self.toggleAbortAction(False)
        self.onToggleImmediate()

        try:
            import psyco
            psyco.cannotcompile(self.onPrint)
        except:
            pass
        
        self.abortaction = False
        self.jobtimer = wx.Timer(self, -1)
        self.Bind(wx.EVT_TIMER, self.onJobTimer, self.jobtimer)
        # started in onScheduleOffset()

        self.inittimer = wx.Timer(self, -1) 
        self.Bind(wx.EVT_TIMER, self.onInitDirCtrl, self.inittimer) 
        self.inittimer.Start(150, oneShot = True)
        
        self.offsettimer = wx.Timer(self, -1)
        self.Bind(wx.EVT_TIMER, self.onScheduleOffset, self.offsettimer) 
        self.offsettimer.Start(int(self.config['Execution']['offset']), oneShot = True)
        
        if self.config['Misc']['update'] == 'True':
            self.updatecheck = True
            self.updatetimer = wx.Timer(self, -1) 
            self.Bind(wx.EVT_TIMER, self.onUpdate, self.updatetimer) 
            self.updatetimer.Start(4000, oneShot = True)
        else:
            self.updatecheck = False

        self.mintimer = wx.Timer(self, -1)
        self.Bind(wx.EVT_TIMER, self.onSysTray, self.mintimer)
        if self.config['Execution']['startmin'] == 'True':
            self.mintimer.Start(5000, oneShot = True)                    
    #-------------------------------------------------------------------------------------------

    def initMenu(self):
        self.mainmenu = wx.MenuBar()         
        # File menu
        menu = wx.Menu()                     
        item = wx.MenuItem(menu, self.ID_REMOVE_ITEM, '&Remove item from storage\tCTRL-R', 'remove item from storage device')
        item.SetBitmap(loadIcon('removeitem.png'))
        menu.AppendItem(item)
        self.Bind(wx.EVT_MENU, self.onRemoveItem, id = self.ID_REMOVE_ITEM)
        menu.AppendSeparator() 
        item = wx.MenuItem(menu, self.ID_EXPORT, '&Export matches\tCTRL-E', 'export matches to file')
        item.SetBitmap(loadIcon('export.png'))
        menu.AppendItem(item)
        self.Bind(wx.EVT_MENU, self.onExport, id = self.ID_EXPORT)
        item = wx.MenuItem(menu, self.ID_PRINT, '&Print matches\tCTRL-P', 'print matches')
        item.SetBitmap(loadIcon('print.png'))
        menu.AppendItem(item)
        self.Bind(wx.EVT_MENU, self.onPrint, id = self.ID_PRINT)
        menu.AppendSeparator()       
        item = wx.MenuItem(menu, self.ID_ABORT_ACTION, '&Abort action\tCTRL-A', 'abort current action')
        item.SetBitmap(loadIcon('abort.png'))                 
        menu.AppendItem(item)
        self.Bind(wx.EVT_MENU, self.onAbortAction, id = self.ID_ABORT_ACTION)
        menu.AppendSeparator()       
        item = wx.MenuItem(menu, self.ID_EXIT, 'E&xit StoreMan\tCTRL-X', 'exit the application')
        item.SetBitmap(loadIcon('exit.png'))                 
        menu.AppendItem(item)
        self.Bind(wx.EVT_MENU, self.onCloseWindow, id = self.ID_EXIT)
        self.mainmenu.Append(menu, '&File')
        
        # View menu
        menu = wx.Menu()
        menu.Append(self.ID_JOBWIN, '&Backup jobs\tCTRL-B', 'show backup jobs list window', wx.ITEM_CHECK)
        self.Bind(wx.EVT_MENU, self.onJobWin, id = self.ID_JOBWIN)        
        menu.Append(self.ID_SCANWIN, 'Sca&n dirs\tCTRL-N', 'show scan dirs window', wx.ITEM_CHECK)
        self.Bind(wx.EVT_MENU, self.onScanWin, id = self.ID_SCANWIN)
        menu.Append(self.ID_DBWIN, 'Databases', 'show db-history window', wx.ITEM_CHECK)
        self.Bind(wx.EVT_MENU, self.onHistWin, id = self.ID_DBWIN)
        menu.Append(self.ID_LOGWIN, '&Log messages\tCTRL-L', 'show log window', wx.ITEM_CHECK)
        self.Bind(wx.EVT_MENU, self.onLogWin, id = self.ID_LOGWIN)
        menu.AppendSeparator()
        menu.Append(self.ID_INI_PERSP, '&Default perspective\tF5', 'set views to default perspective')
        self.Bind(wx.EVT_MENU, self.onPerspective, id = self.ID_INI_PERSP)
        self.mainmenu.Append(menu, '&View')

        # Backup menu 
        menu = wx.Menu()               
        item = wx.MenuItem(menu, self.ID_MANAGE_TASKS, 'Manage &task(s)\tCTRL-T', 'add, edit or remove task(s)')
        item.SetBitmap(loadIcon('tasksman.png'))
        menu.AppendItem(item)
        self.Bind(wx.EVT_MENU, self.onManageTasks, id = self.ID_MANAGE_TASKS)
        item = wx.MenuItem(menu, self.ID_MANAGE_JOBS, 'Manage &job(s)\tCTRL-J', 'add, edit or remove job(s)')
        item.SetBitmap(loadIcon('jobsman.png'))
        menu.AppendItem(item)
        self.Bind(wx.EVT_MENU, self.onManageJobs, id = self.ID_MANAGE_JOBS)
        menu.AppendSeparator()
        item = wx.MenuItem(menu, self.ID_IMMEDIATE_EXEC, 'Immediate &job execution\tSHIFT-CTRL-J', 'execute ticked task(s)')
        item.SetBitmap(loadIcon('backup.png'))         
        menu.AppendItem(item)
        self.Bind(wx.EVT_MENU, self.onImmediateExecution, id = self.ID_IMMEDIATE_EXEC)
        self.mainmenu.Append(menu, '&Backup')

        # Scan menu
        menu = wx.Menu()
        item = wx.MenuItem(menu, self.ID_MANAGE_SCAN, 'Manage &scan dirs\tCTRL-S', 'add or remove scan dirs')
        item.SetBitmap(loadIcon('scanman.png'))
        menu.AppendItem(item)
        self.Bind(wx.EVT_MENU, self.onManageScan, id = self.ID_MANAGE_SCAN)
        menu.AppendSeparator()
        item = wx.MenuItem(menu, self.ID_EXECUTE_SCAN, 'Execute &scan\tSHIFT-CTRL-S', 'scan dirs to database')
        item.SetBitmap(loadIcon('scan.png'))         
        menu.AppendItem(item)
        self.Bind(wx.EVT_MENU, self.onScan, id = self.ID_EXECUTE_SCAN)
        self.mainmenu.Append(menu, '&Scan')

        # Database menu
        menu = wx.Menu()
        item = wx.MenuItem(menu, self.ID_MANAGE_DB, 'Manage databases\tCTRL-D', 'add, init, open or remove database')
        item.SetBitmap(loadIcon('dbman.png'))
        menu.AppendItem(item)
        self.Bind(wx.EVT_MENU, self.onManageDb, id = self.ID_MANAGE_DB)        
        menu.AppendSeparator()        
        item = wx.MenuItem(menu, self.ID_QUERY, '&Query database\tCTRL-Q', 'execute query')
        item.SetBitmap(loadIcon('query.png'))         
        menu.AppendItem(item)
        self.Bind(wx.EVT_MENU, self.onQuery, id = self.ID_QUERY)        
        self.mainmenu.Append(menu, '&Database')
        
        # Options menu
        menu = wx.Menu()
        item = wx.MenuItem(menu, self.ID_PREFS, 'Pre&ferences\tCTRL-F', 'set preferences')
        item.SetBitmap(loadIcon('prefs.png'))
        menu.AppendItem(item)
        self.Bind(wx.EVT_MENU, self.onPreferences, id = self.ID_PREFS)        
        item = wx.MenuItem(menu, self.ID_MOUNTMAN, '(Un)Mount fs\tCTRL-M', '(un)mount fs tab listed device')
        item.SetBitmap(loadIcon('mountman.png'))         
        menu.AppendItem(item)
        self.Bind(wx.EVT_MENU, self.onMountMan, id = self.ID_MOUNTMAN)
        item.Enable(LINUX)
        menu.AppendSeparator()
        item = wx.MenuItem(menu, self.ID_SYSTRAY, 'S&ystray icon\tCTRL-Y', 'minimize app to systray')
        item.SetBitmap(loadIcon('systray.png'))
        menu.AppendItem(item)
        self.Bind(wx.EVT_MENU, self.onSysTray, id = self.ID_SYSTRAY)
        self.mainmenu.Append(menu, '&Options')
        
        # Help menu
        menu = wx.Menu()
        item = wx.MenuItem(menu, self.ID_HELP, '&Help\tF1', 'html help docs')
        item.SetBitmap(loadIcon('help.png'))  
        menu.AppendItem(item)
        self.Bind(wx.EVT_MENU, self.onHandbook, id = self.ID_HELP)
        menu.AppendSeparator()
        item = wx.MenuItem(menu, self.ID_WEBSITE, 'StoreMan &Website\tCTRL-W', 'StoreMan Website')
        item.SetBitmap(loadIcon('website.png'))  
        menu.AppendItem(item)
        self.Bind(wx.EVT_MENU, self.onWebsite, id = self.ID_WEBSITE)
        item = wx.MenuItem(menu, self.ID_UPDATE, 'StoreMan &Updates\tCTRL-U', 'Download newest version')
        item.SetBitmap(loadIcon('update.png'))  
        menu.AppendItem(item)
        self.Bind(wx.EVT_MENU, self.onUpdate, id = self.ID_UPDATE)        
        item = wx.MenuItem(menu, self.ID_INSTALL, '&install StoreMan Updates\tCTRL-I', 'install newest version')
        item.SetBitmap(loadIcon('update.png'))  
        menu.AppendItem(item)
        self.Bind(wx.EVT_MENU, self.onInstallUpdate, id = self.ID_INSTALL)               
        menu.AppendSeparator()  
        item = wx.MenuItem(menu, self.ID_ABOUT, 'A&bout\tCTRL-B', 'about the application')
        item.SetBitmap(loadIcon('about.png'))  
        menu.AppendItem(item)
        self.Bind(wx.EVT_MENU, self.onAbout, id = self.ID_ABOUT)
        self.mainmenu.Append(menu, '&Help')

        self.SetMenuBar(self.mainmenu)
        
        self.mainmenu.Check(self.ID_SCANWIN, self.config['View']['scanwin'] == 'True')
        self.mainmenu.Check(self.ID_DBWIN, self.config['View']['histwin'] == 'True')
        self.mainmenu.Check(self.ID_JOBWIN, self.config['View']['jobwin'] == 'True')       
        self.mainmenu.Check(self.ID_LOGWIN, self.config['View']['logwin'] == 'True')        
    #-------------------------------------------------------------------------------------------
    
    def initToolbar(self):        
        self.tb = self.CreateToolBar(wx.TB_HORIZONTAL | wx.NO_BORDER | wx.TB_FLAT | wx.TB_TEXT)

        self.tb.AddSimpleTool(self.ID_IMMEDIATE_EXEC, loadIcon('backup.png'), 'immediate job execution', 'immediate job execution')
        self.tb.AddSimpleTool(self.ID_EXECUTE_SCAN, loadIcon('scan.png'), 'scan dirs', 'scan dirs')
        self.tb.AddSimpleTool(self.ID_ABORT_ACTION, loadIcon('abort.png'), 'abort action', 'abort action')
        self.tb.AddSimpleTool(self.ID_QUERY, loadIcon('query.png'), 'execute query', 'execute query')
        self.tb.AddSimpleTool(self.ID_SYSTRAY, loadIcon('systray.png'), 'Systray icon', 'Systray icon')
        self.tb.AddSimpleTool(self.ID_HELP, loadIcon('help.png'), 'Help', 'Help')
#22
        self.tb.AddSeparator()
        self.tb.AddControl(wx.StaticText(self.tb, -1, "  Directory: "))
        self.cbdirname = wx.ComboBox(self.tb, self.ID_CBDIRNAME, self.config['Query']['dirmask'][0],
                                     size = wx.Size(167, -1), choices = self.config['Query']['dirmask'], style = wx.TE_PROCESS_ENTER)
        self.tb.AddControl(self.cbdirname)
        self.Bind(wx.EVT_TEXT_ENTER, self.onQuery)
        self.tb.AddSeparator()

        self.tb.AddControl(wx.StaticText(self.tb, -1, "File: "))
#22            
        self.cbfilename = wx.ComboBox(self.tb, self.ID_CBFILENAME, self.config['Query']['filemask'][0],
                                      size = wx.Size(167, -1), choices = self.config['Query']['filemask'],  style = wx.TE_PROCESS_ENTER)
        self.tb.AddControl(self.cbfilename)
        self.Bind(wx.EVT_TEXT_ENTER, self.onQuery)
        self.tb.AddSeparator()
#22
        rb1 = wx.RadioButton(self.tb, self.ID_RADIOBUTTON1, '>= ', size = wx.Size(45, -1),
                             style = wx.RB_GROUP)
        self.Bind(wx.EVT_RADIOBUTTON, self.onRadioBox, id = self.ID_RADIOBUTTON1)
        self.tb.AddControl(rb1)
        rb1.SetValue(self.config['Query']['compare'] == '>=')
#22        
        rb2 = wx.RadioButton(self.tb, self.ID_RADIOBUTTON2, '<=', size = wx.Size(50, -1))
        self.Bind(wx.EVT_RADIOBUTTON, self.onRadioBox, id = self.ID_RADIOBUTTON2)
        self.tb.AddControl(rb2)
        rb2.SetValue(self.config['Query']['compare'] == '<=')
        self.compare = self.config['Query']['compare']
#22
        self.tcfilesize = wx.lib.intctrl.IntCtrl(self.tb, value = int(self.config['Query']['sizemask']),
                                                 size = wx.Size(60, -1), style = wx.TE_PROCESS_ENTER)
        self.tb.AddControl(self.tcfilesize)
        self.Bind(wx.EVT_TEXT_ENTER, self.onQuery)
        self.unittxt = wx.StaticText(self.tb, -1, ' ' + self.config['Misc']['unit'])
        self.tb.AddControl(self.unittxt)
        self.tb.AddSeparator()
#22
        self.tb.AddControl(wx.StaticText(self.tb, -1, "Date: "))
        self.cbfiledate = wx.ComboBox(self.tb, self.ID_CBFILEDATE, self.config['Query']['datemask'][0],
                                      size = wx.Size(105, -1), choices = self.config['Query']['datemask'], style = wx.TE_PROCESS_ENTER)
        self.tb.AddControl(self.cbfiledate)
        self.Bind(wx.EVT_TEXT_ENTER, self.onQuery)

        self.tb.Realize()
    #-------------------------------------------------------------------------------------------
    
    def initContextMenu(self):
        self.popupID0 = wx.NewId()  
        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.popupID8 = wx.NewId()
        self.popupID9 = wx.NewId()
        self.popupID10 = wx.NewId()
        self.popupID11 = wx.NewId()
        
        self.Bind(wx.EVT_MENU, self.onRemoveItem, id = self.popupID0)
        self.Bind(wx.EVT_MENU, self.onQuery, id = self.popupID1)
        self.Bind(wx.EVT_MENU, self.onManageDb, id = self.popupID2)
        self.Bind(wx.EVT_MENU, self.onManageJobs, id = self.popupID3)
        self.Bind(wx.EVT_MENU, self.onManageTasks, id = self.popupID4)
        self.Bind(wx.EVT_MENU, self.onImmediateExecution, id = self.popupID5)
        self.Bind(wx.EVT_MENU, self.onManageScan, id = self.popupID6)
        self.Bind(wx.EVT_MENU, self.onScan, id = self.popupID7)
        self.Bind(wx.EVT_MENU, self.onExport, id = self.popupID8)
        self.Bind(wx.EVT_MENU, self.onPrint, id = self.popupID9)
        self.Bind(wx.EVT_MENU, self.onPreferences, id = self.popupID10)
        self.Bind(wx.EVT_MENU, self.onSysTray, id = self.popupID11)
    #-------------------------------------------------------------------------------------------

    def onContext(self, event):
        """ context menu handler """
        menu = wx.Menu()
        item = wx.MenuItem(menu, self.popupID4, 'Manage tasks')
        item.SetBitmap(loadIcon('tasksman.png'))
        menu.AppendItem(item)
        item = wx.MenuItem(menu, self.popupID3, 'Manage jobs')
        item.SetBitmap(loadIcon('jobsman.png'))
        menu.AppendItem(item)
        item = wx.MenuItem(menu, self.popupID5, 'Immediate job exec')
        item.SetBitmap(loadIcon('backup.png'))
        menu.AppendItem(item)
        menu.AppendSeparator()        
        item = wx.MenuItem(menu, self.popupID6, 'Manage scan dirs')
        item.SetBitmap(loadIcon('scanman.png'))
        menu.AppendItem(item)
        item = wx.MenuItem(menu, self.popupID7, 'Scan dirs')
        item.SetBitmap(loadIcon('scan.png'))
        menu.AppendItem(item)
        menu.AppendSeparator()        
        item = wx.MenuItem(menu, self.popupID2, 'Manage databases')
        item.SetBitmap(loadIcon('dbman.png'))         
        menu.AppendItem(item)
        item = wx.MenuItem(menu, self.popupID1, 'Query database')
        item.SetBitmap(loadIcon('query.png'))        
        menu.AppendItem(item)
        menu.AppendSeparator()        
        item = wx.MenuItem(menu, self.popupID0, 'Remove item from storage')
        item.SetBitmap(loadIcon('removeitem.png'))
        menu.AppendItem(item)        
        item = wx.MenuItem(menu, self.popupID8, 'Export matches')
        item.SetBitmap(loadIcon('export.png'))
        menu.AppendItem(item)
        item = wx.MenuItem(menu, self.popupID9, 'Print matches')
        item.SetBitmap(loadIcon('print.png'))
        menu.AppendItem(item)       
        menu.AppendSeparator()
        item = wx.MenuItem(menu, self.popupID10, 'Preferences')
        item.SetBitmap(loadIcon('prefs.png'))
        menu.AppendItem(item)
        item = wx.MenuItem(menu, self.popupID11, 'Systray icon')
        item.SetBitmap(loadIcon('systray.png'))
        menu.AppendItem(item)
        self.PopupMenu(menu)
        menu.Destroy()
    #-------------------------------------------------------------------------------------------      
     
    def onScheduleOffset(self, event):
        ''' start scheduler with a delay of self.config['Execution']['offset'] '''
        self.jobtimer.Start(JOB_TIME_SLICE)
    #-------------------------------------------------------------------------------------------

    def onInitDirCtrl(self, event):
        ''' avoid the dir dialog delay on linux '''
        dirctrl = wx.GenericDirCtrl(self, size = wx.Size(1, 1), style = wx.DIRCTRL_DIR_ONLY)
        dirctrl.Show(False)         # on win32 not needed
    #-------------------------------------------------------------------------------------------

    def onMountMan(self, event):        
        dlg = stomamountman.MountManDialog(self, self.config, self.writeToLogwin)
        dlg.ShowModal()
    #-------------------------------------------------------------------------------------------

    def onDbwin(self, event):
        index = event.GetIndex()
        self.config['Database']['index'] = index
        self.connect2Database()
    #-------------------------------------------------------------------------------------------
 
    def onBackupClicked(self, event):
        value = self.jobwin.IsChecked(event.Int)
        self.jobwin.Check(event.Int, not value)
    #-------------------------------------------------------------------------------------------
   
    def updateDbwin(self):
        current = self.config['Database']['dbhistory'][int(self.config['Database']['index'])]
        dbpaths = self.config['Database']['dbhistory']
        dbpaths.sort()
        self.config['Database']['dbhistory'] = dbpaths
        sortindex = self.config['Database']['dbhistory'].index(current)
        self.config['Database']['index'] = sortindex
        
        self.dbwin.DeleteAllItems()
        for index, dbpath in enumerate(dbpaths):
            head, tail = os.path.split(dbpath)
            self.dbwin.InsertStringItem(index, tail)
            if self.stomadb != None and index == sortindex:
                self.dbwin.SetItemBackgroundColour(index, self.hlcolor)
        x, y = self.dbwin.GetSizeTuple()                # fix for win32
        self.dbwin.SetColumnWidth(0, x)                 # fix for win32
    #-------------------------------------------------------------------------------------------
    
    def onPerspective(self, event):
        self._mgr.LoadPerspective(self.defaultperspective)
        self.config['View']['scanwin'] = 'True'
        self.config['View']['histwin'] = 'True'
        self.config['View']['jobwin'] = 'True'
        self.config['View']['logwin'] = 'True'
        self.mainmenu.Check(self.ID_SCANWIN, True)
        self.mainmenu.Check(self.ID_DBWIN, True)
        self.mainmenu.Check(self.ID_JOBWIN, True)       
        self.mainmenu.Check(self.ID_LOGWIN, True)        
    #-------------------------------------------------------------------------------------------
        
    def onPaneClose(self, event):
        mb = self.GetMenuBar()
        name = event.GetPane().name
        if name == 'scanwin':
            submenuitem = mb.FindItemById(self.ID_SCANWIN)
            self.config['View']['scanwin'] = 'False'
        elif name == 'histwin':
            submenuitem = mb.FindItemById(self.ID_DBWIN)
            self.config['View']['histwin'] = 'False'
        elif name == 'jobwin':
            submenuitem = mb.FindItemById(self.ID_JOBWIN)
            self.config['View']['jobwin'] = 'False'
        else:
            submenuitem = mb.FindItemById(self.ID_LOGWIN)
            self.config['View']['logwin'] = 'False'
        submenuitem.Check(False)    
    #-------------------------------------------------------------------------------------------

    def onScanWin(self, event):
        mb = self.GetMenuBar()
        submenuitem = mb.FindItemById(self.ID_SCANWIN)
        if submenuitem.IsChecked():
            self._mgr.GetPane('scanwin').Show()
        else:
            self._mgr.GetPane('scanwin').Hide()
        self.config['View']['scanwin'] = str(submenuitem.IsChecked())
        self._mgr.Update()
    #-------------------------------------------------------------------------------------------

    def onHistWin(self, event):
        mb = self.GetMenuBar()
        submenuitem = mb.FindItemById(self.ID_DBWIN)
        if submenuitem.IsChecked():
            self._mgr.GetPane('histwin').Show()
        else:
            self._mgr.GetPane('histwin').Hide()
        self.config['View']['histwin'] = str(submenuitem.IsChecked())            
        self._mgr.Update()
    #-------------------------------------------------------------------------------------------

    def onJobWin(self, event):
        mb = self.GetMenuBar()
        submenuitem = mb.FindItemById(self.ID_JOBWIN)
        if submenuitem.IsChecked():
            self._mgr.GetPane('jobwin').Show()
        else:
            self._mgr.GetPane('jobwin').Hide()
        self.config['View']['jobwin'] = str(submenuitem.IsChecked())            
        self._mgr.Update()
    #-------------------------------------------------------------------------------------------

    def onLogWin(self, event):
        mb = self.GetMenuBar()
        submenuitem = mb.FindItemById(self.ID_LOGWIN)
        if submenuitem.IsChecked():
            self._mgr.GetPane('logwin').Show()
        else:
            self._mgr.GetPane('logwin').Hide()
        self.config['View']['logwin'] = str(submenuitem.IsChecked())
        self._mgr.Update()
    #-------------------------------------------------------------------------------------------

    def onItemSelected(self, event):
        self.selitemindex = event.Index
        if wx.TheClipboard.Open():
            wx.TheClipboard.SetData(wx.TextDataObject(self.listwin.getColumnText(self.selitemindex, 0)))
            wx.TheClipboard.Close()
    #-------------------------------------------------------------------------------------------

    def onQueryItemDoubleClick(self, event):
        cmdline = self.config['ListCtrl']['fileman'][0]
        fileman = cmdline.split()[0]
        searchpaths = os.path.expandvars('$PATH')
            
        if not searchFile(fileman, searchpaths):
            dlg = wx.MessageDialog(self, "unable to execute command\n'" + cmdline + "'\nFilemanager possibly not in searchpath!", TITLE + ' - start file manager', wx.OK | wx.ICON_ERROR)
            dlg.ShowModal()
            return
                
        if cmdline.find('%dir') < 0:
            dlg = wx.MessageDialog(self, "command line syntax error - placeholder '%dir' missing", TITLE + ' - run file manager', wx.OK | wx.ICON_ERROR)
            dlg.ShowModal()
            return
        
        path = os.path.normpath(self.listwin.getColumnText(event.Index, 0) + '/')
        path += ' &'
        cmdline = cmdline.replace('%dir', path)
            
        os.system(cmdline)
    #-------------------------------------------------------------------------------------------

    def onInstallUpdate(self, event):
        # install new program release
        dlg = stomainstall.InstallDialog(self, self.config, self.writeToLogwin, self.newrelease)
        dlg.ShowModal()
        self.mainmenu.Enable(self.ID_INSTALL, False)
    #-------------------------------------------------------------------------------------------

    def readUrl(self):    
        try:
            f = urllib2.urlopen(URL_DOWNLOAD_LIST, timeout = 6)
            html = f.read()
            f.close()
        except:
            return         
        
        versions = []
        while len(html):
            fpos = html.find('StoreMan-')
            html = html[fpos:]
            version = html[9:13]
            if html[13:17] == '.zip':
                versions.append(version)
            html = html[24:]
            
        versions.sort(reverse = True) 
        self.versions = versions
    #-------------------------------------------------------------------------------------------

    def onUpdate(self, event):
        self.versions = -1
        self.writeToLogwin('Update - try to connect to StoreMan website')

        updatethread = threading.Thread(target = self.readUrl, args = ())
        updatethread.start()
        while updatethread.isAlive():
            try:
                wx.SafeYield(self)
            except:
                pass
        
        if type(self.versions) == int:
            dlg = wx.MessageDialog(self, 'unable to connect to the StoreMan website!', TITLE + ' - find update', wx.OK | wx.ICON_ERROR)
            dlg.ShowModal()
            return            
        
        if len(self.versions):
            self.versions.sort(reverse = True) 
            version = self.versions[0]
            if version > VERNUM[:4]:
                dlg = wx.MessageDialog(self, "a newer version (%s) of StoreMan is available!" % (version), TITLE + ' - find Storeman update', wx.OK | wx.ICON_EXCLAMATION)
                dlg.ShowModal()
                self.mainmenu.Enable(self.ID_INSTALL, True)
                self.newrelease = '%s/%s-%s.zip' % (URL_DOWNLOAD_FILES, TITLE, version)
                self.writeToLogwin("Update check - a newer version of StoreMan is available!") 
                return
        else:
            self.writeToLogwin("Update check - cannot read information from StoreMan website")
            return
            
        if self.updatecheck:
            self.updatecheck = False
            self.writeToLogwin("Update check - the installed version of StoreMan is up to date")
        else:    
            dlg = wx.MessageDialog(self, 'the installed version of StoreMan is up to date!', TITLE + ' - find update', wx.OK | wx.ICON_INFORMATION)
            dlg.ShowModal()
    #-------------------------------------------------------------------------------------------

    def onWebsite(self, event):
        try:
            webbrowser.open(URL_WEBSITE)
        except:
            dlg = wx.MessageDialog(self, 'cannot connect to the StoreMan website!', TITLE + ' - browse website', wx.OK | wx.ICON_ERROR)
            dlg.ShowModal()
    #-------------------------------------------------------------------------------------------

    def onRemoveItem(self, event):
        if self.selitemindex == -1:
            dlg = wx.MessageDialog(self, 'there is no item to remove selected!', TITLE + ' - remove item', wx.OK | wx.ICON_ERROR)
            dlg.ShowModal()
            return
        record = self.records[self.selitemindex]
        path = os.path.join(record[0], record[1])
        if self.config['ListCtrl']['confirm'] == 'True':
            dlg = wx.MessageDialog(self, "do you really want to remove item\n'%s'\nfrom storage device and database?" % (path), TITLE + ' - remove item', wx.YES_NO | wx.NO_DEFAULT | wx.ICON_EXCLAMATION)
            if dlg.ShowModal() != wx.ID_YES: return

        try:
            os.remove(path)
            self.writeToLogwin("Remove item - file '%s' removed from database and erased from storage" % (path))
        except:
            dlg = wx.MessageDialog(self, "unable to remove\n'%s'\nfrom storage device!" % (path), TITLE + ' - remove item', wx.OK | wx.ICON_ERROR)
            dlg.ShowModal()
            return
            
        self.stomadb.deleteRecordById(record[4])
        self.onQuery()                                  # update Display by re-query
    #-------------------------------------------------------------------------------------------

    def updateDbName(self, recordcount = None):
        index = int(self.config['Database']['index'])
        self.sb.SetStatusText(self.config['Database']['dbhistory'][index], 3)
        if recordcount != None:
            self.updateRecords(recordcount)
        if self.stomadb != None:
            self.sb.setLed(GREEN)
        else:
            self.sb.setLed(RED)
    #-------------------------------------------------------------------------------------------

    def updateUnits(self):
        self.unittxt.SetLabel(' ' + self.config['Misc']['unit'])
    #-------------------------------------------------------------------------------------------

    def updateRecords(self, recordcount = 0):
        self.sb.SetStatusText('R: %s' % (format3P(recordcount)), 2)
        wx.SafeYield(self)
    #-------------------------------------------------------------------------------------------
    
    def clearDisplay(self):
        self.listwin.DeleteAllItems()
        self.selitemindex = -1
    #-------------------------------------------------------------------------------------------
    
    def updateMatches(self, matchesread = 0):
        self.sb.SetStatusText('M: %s' % (format3P(matchesread)), 2)
    #-------------------------------------------------------------------------------------------

    def updateBackupFiles(self, filecount = 0, skipcount = 0, errorcount = 0):
        self.sb.SetStatusText('B: %s, %s, %s' % (format3P(filecount), format3P(skipcount), format3P(errorcount)), 2)
        wx.SafeYield(self)
    #-------------------------------------------------------------------------------------------

    def updateScanList(self):
        self.scanwin.DeleteAllItems()
        i = 0
        if self.stomadb != None:
            scandates = self.stomadb.getScanDates()
        else:
            scandates = []

        for item in self.config['Scan']['scandirs']:        
            self.scanwin.InsertStringItem(i, item)
            i += 1
        if len(self.config['Scan']['scandirs']) or len(scandates):
            if LINUX:
                self.scanwin.InsertStringItem(i, '—— db scan list ——')
            else:
                self.scanwin.InsertStringItem(i, '---- db scan list ----')
            self.scanwin.SetItemTextColour(i, GREY)
            i += 1   

        for item in scandates:
            self.scanwin.InsertStringItem(i, item[1])
            self.scanwin.SetItemTextColour(i, GREY)
            i += 1
        if LINUX:
            self.scanwin.SetColumnWidth(0, wx.LIST_AUTOSIZE)
        else:
            x, y = self.scanwin.GetSizeTuple()                # fix for win32
            self.scanwin.SetColumnWidth(0, x)                 # fix for win32                                    
    #-------------------------------------------------------------------------------------------

    def onCollapseOrExpand(self, event = None):
        jobnames = self.config['Jobs'].keys()
        jobnames.sort()
        self.config['Window']['tcexpstate'] = self.jobwin.GetExpansionState()
        self.expnodelist = []
        for item in self.config['Window']['tcexpstate']:
            self.expnodelist.append(jobnames[item[0]])
    #-------------------------------------------------------------------------------------------
            
    def updateJobwin(self, withcount = False):
        jobnames = self.config['Jobs'].keys()
        jobnames.sort()
        immcount = 0
        backcount = 0
        expnodelist = self.expnodelist          # make local copy because list changes
        
        self.jobwin.DeleteAllItems()            
        root = self.jobwin.AddRoot("Backup jobs", 0)
        for index, jobname in enumerate(jobnames):        
            item = self.jobwin.AppendItem(root, jobname, 0)
            if self.config['Jobs'][jobname]['immediate'] == 'True':
                self.jobwin.SetItemBackgroundColour(item, self.hlcolor)
                immcount += 1
            if self.config['Jobs'][jobname]['background'] == 'True':
                backcount += 1
            for task in self.config['Jobs'][jobname]['tasklist']:
                self.jobwin.AppendItem(item, task, 1)
            if jobname in expnodelist:
                self.jobwin.Expand(item)

        if withcount:
            self.writeToLogwin("Job status - %d / %d job(s) are ready for immediate / scheduled excecution" % (immcount, backcount))                    
    #-------------------------------------------------------------------------------------------

    def writeToLogwin(self, info, color = None):
        try:
            start = self.logwin.GetInsertionPoint()
            self.logwin.WriteText('%s\n' % (info))
            stop = self.logwin.GetInsertionPoint() - 1
            if color != None:
                self.logwin.SetStyle(start, stop, wx.TextAttr(color))
            self.logwin.ShowPosition(-1)
            if self.config['Misc']['actionlog'] == 'True':
                self.logger.info('%s' % (info))
        except:
            pass
    #-------------------------------------------------------------------------------------------

    def connect2Database(self):
        self.writeToLogwin('DB-connect - try to connect to database')
        self.clearDisplay()

        if self.stomadb != None:
            self.stomadb.commitTransaction()
            self.stomadb = None
            self.sb.setLed(YELLOW)
        
        index = int(self.config['Database']['index'])
        try:
            dbfilename = self.config['Database']['dbhistory'][index]
            if not os.path.exists(dbfilename) and dbfilename.endswith('default.db3'):
                f = file(dbfilename, 'wb')    # create db file 
                f.close()
            f = file(dbfilename, 'rb')
            f.close()
            
            self.stomadb = stomasqlite.StoreManDb(dbfilename, self.updateRecords, self.updateMatches)
            if not self.stomadb.checkDatabase()[0]:
                self.stomadb = None
                self.writeToLogwin("DB-connect - could connect to database '%s', but its not a StoreMan db" % (dbfilename), RED)
                self.updateDbName(None)
                dlg = wx.MessageDialog(self, "This is a db3 but not a StoreMan database!", TITLE + ' - open database', wx.OK | wx.ICON_ERROR)
                dlg.ShowModal()
                dlg.Destroy()
            else:
                self.updateDbName(self.stomadb.getRecordCount())
                self.dbiswritable = os.access(dbfilename, os.W_OK)
                if self.dbiswritable:    
                    self.writeToLogwin("DB-connect - database '%s' opened" % (dbfilename))
                else:
                    self.writeToLogwin("DB-connect - database '%s' opened (but db is not writable)" % (dbfilename), RED)
        except:
            self.writeToLogwin("DB-connect - could not connect to database '%s'" % (dbfilename), RED)
            self.sb.SetStatusText('not connected', 3)
            self.sb.setLed(RED)
        self.updateDbwin()
        self.updateScanList()
    #-------------------------------------------------------------------------------------------

    def onManageDb(self, event):
        if self.stomadb != None:
            self.stomadb.commitTransaction()
        dlg = stomadbman.DbManDialog(self, self.updateDbwin, self.writeToLogwin, self.connect2Database,
                                     self.config, self.jobtimer)
        dlg.ShowModal()
    #-------------------------------------------------------------------------------------------

    def onRadioBox(self, event):
        if event.Id == self.ID_RADIOBUTTON1:
            self.compare = '>='
        else:
            self.compare = '<='
        self.config['Query']['compare'] = self.compare
    #-------------------------------------------------------------------------------------------

    def onPreferences(self, event):
        """  """
        self.handleConfigData('w')
        prefdlg = stomaprefs.PrefsDialog(self, self.hlcolor, self.inifilename)
        if prefdlg.ShowModal() == wx.ID_OK:
            self.config = prefdlg.getNewConfig()
            self.config.write()
            self.writeToLogwin('Preferences - new values set')
        else:
            self.config.reload()
        prefdlg.Destroy()
        self.updateUnits()
        self.myfont = wx.Font(int(self.config['ListCtrl']['fsize']),
                              wx.DEFAULT, wx.NORMAL, wx.NORMAL, False,
                              self.config['ListCtrl']['font'])
        self.listwin.SetFont(self.myfont)
        collist = self.config['Misc']['hlcolor']
        newhlcolor = (int(collist[0]), int(collist[1]), int(collist[2]))
        if newhlcolor != self.hlcolor:
            self.hlcolor = newhlcolor
            self.updateDbwin()
            self.updateJobwin()
    #-------------------------------------------------------------------------------------------

    def onManageScan(self, event):
        dlg = stomascanman.ManageScanDirsDialog(self, self.updateScanList, self.writeToLogwin, self.config)
        dlg.ShowModal()
        dlg.Destroy()
        self.updateScanList()
    #-------------------------------------------------------------------------------------------       
        
    def onScan(self, event):
        self.toggleAbortAction(True)
        if self.stomadb != None and self.dbiswritable:
            scandirs = self.config['Scan']['scandirs']
            if len(scandirs):
                result = self.stomadb.getScanDates()
                dbscandirs = []
                for item in result:
                    if item[1] in scandirs:
                        dbscandirs.append(item[1])        
        
                if len(dbscandirs):
                    msg = 'You already scanned the following devices / dirs:\n\n'
                    for entry in dbscandirs:
                        msg = msg + "'" + entry + "'\n"
                    msg += '\nIf you rescan the same devices / dirs you may have\nduplicated database entries. Is this what you really want to do?'
                    dlg = wx.MessageDialog(self, msg, TITLE + ' - Scan directories',  wx.NO  | wx.NO_DEFAULT  | wx.YES | wx.ICON_WARNING)
                    if dlg.ShowModal() != wx.ID_YES: return
                    
                self.clearDisplay()
                self.sb.setLed(BLUE)
                wx.SafeYield(self)
                
                for scandir in scandirs:
                    if self.abortaction: break
                    self.writeToLogwin("Scan - scanning directory '%s'" % (scandir))
                    scan = stomasqlite.ScanDir(self.getAbortAction, scandir, self.stomadb,
                                               self.sb.SetStatusText,
                                               self.config['Misc']['priority'] == 'True')
                    scan.scanDirs()
                if self.abortaction:
                    self.writeToLogwin('Scan - action aborted by user', RED)
                else:
                    self.writeToLogwin('Scan - scanning done')
                self.updateDbName(self.stomadb.getRecordCount())
            else:
                self.writeToLogwin('Scan - nothing to do (no scandir given)')
            self.updateScanList()
            self.stomadb.commitTransaction()
        else:
            self.writeToLogwin("Scan - can't scan (no db connection or db not writable)")
        self.toggleAbortAction(False)
    #-------------------------------------------------------------------------------------------

    def stackListItem(self, indexstr, txtitem, comboctrl):
        if txtitem != self.config['Query'][indexstr][0]:
            if txtitem in self.config['Query'][indexstr]:
                self.config['Query'][indexstr].remove(txtitem)
            self.config['Query'][indexstr].insert(0, txtitem)
            if len(self.config['Query'][indexstr]) == 9:
                del self.config['Query'][indexstr][8]
            comboctrl.Clear()
            for item in self.config['Query'][indexstr]:
                comboctrl.Append(item)
            comboctrl.SetValue(txtitem)
    #-------------------------------------------------------------------------------------------

    def onColumnSort(self, event):
        col = event.Column
        orderby = ['dirname', 'filename', 'filesize', 'filedate'][col]

        txtdirname = self.cbdirname.GetValue()
        if not txtdirname:
            self.cbdirname.SetValue('%')
            txtdirname = '%'            
        txtfilename = self.cbfilename.GetValue()
        if not txtfilename:
            self.cbfilename.SetValue('%')
            txtfilename = '%'
        txtfilesize = self.tcfilesize.GetValue()
        txtfiledate = self.cbfiledate.GetValue()
        if not txtfiledate:
            self.cbfiledate.SetValue('%')
            txtfiledate = '%'        
        limit = self.config['Database']['limit']
        querymask = stomasqlite.QueryMask(txtdirname, txtfilename, self.compare, txtfilesize,
                                          self.config['Misc']['unit'], txtfiledate,
                                          self.config['Database']['ignorecase'] == 'True',
                                          orderby, limit)
        limit = int(limit)
        self.clearDisplay()
        if self.stomadb != None:
            self.writeToLogwin('Query - reexecuting query with new sort order')
            self.sb.setLed(YELLOW)
            wx.SafeYield(self)
            self.records, matchcount = self.stomadb.getMatches(querymask)
            self.updateMatches(matchcount)
            self.listwin.setDataSource(self.records, min(matchcount, limit))
            if matchcount > limit:
                self.sb.setLed(LRED)
            else:
                self.sb.setLed(GREEN)        
    #-------------------------------------------------------------------------------------------

    def onQuery(self,  event = None):
        txtdirname = self.cbdirname.GetValue()
        if not txtdirname:
            self.cbdirname.SetValue('%')
            txtdirname = '%'            
        txtfilename = self.cbfilename.GetValue()
        if not txtfilename:
            self.cbfilename.SetValue('%')
            txtfilename = '%'
        txtfilesize = self.tcfilesize.GetValue()
        txtfiledate = self.cbfiledate.GetValue()
        if not txtfiledate:
            self.cbfiledate.SetValue('%')
            txtfiledate = '%'
        
        limit = self.config['Database']['limit']
        querymask = stomasqlite.QueryMask(txtdirname, txtfilename, self.compare, txtfilesize,
                                          self.config['Misc']['unit'], txtfiledate,
                                          self.config['Database']['ignorecase'] == 'True',
                                          self.config['Database']['orderby'], limit)
        limit = int(limit)
        self.clearDisplay()
        if self.stomadb != None:
            self.writeToLogwin('Query - executing query')
            self.sb.setLed(YELLOW)
            wx.SafeYield(self)
            self.records = None
            try:
                self.records, matchcount = self.stomadb.getMatches(querymask)
                self.updateMatches(matchcount)
                self.listwin.setDataSource(self.records, min(matchcount, limit))
                if matchcount > limit:
                    self.sb.setLed(LRED)
                else:
                    self.sb.setLed(GREEN)
                self.stackListItem('dirmask', txtdirname, self.cbdirname)
                self.stackListItem('filemask', txtfilename, self.cbfilename)
                self.config['Query']['sizemask'] = txtfilesize
                self.stackListItem('datemask', txtfiledate, self.cbfiledate)
                if matchcount:
                    self.writeToLogwin('Query - query matches db entries')
                else:
                    self.writeToLogwin('Query - query matches no db entries')
            except:
                dlg = wx.MessageDialog(self, 'db3 file is encrypted or is not a database', TITLE + ' - Query', wx.OK | wx.ICON_ERROR)
                self.sb.setLed(RED)
                dlg.ShowModal()
        else:
            self.writeToLogwin("Query - can't query (no db connection)")
    #-------------------------------------------------------------------------------------------

    def onExport(self, event):
        """  """
        import csvwriter
        
        wildcard = 'csv file (*.csv)|*.csv|' \
                   'All files (*.*)|*.*'

        csvpath = self.config['Misc']['csvname']
        dlg = wx.FileDialog(self, TITLE + ' - save Matches as csv file', '', csvpath, wildcard, wx.FD_SAVE)
        if dlg.ShowModal() == wx.ID_OK:
            csvpath = os.path.normpath(dlg.GetPath())
            if os.path.exists(csvpath):
                dlg = wx.MessageDialog(self, "You have selected an existing csv file. Are you sure\n you want to replace it with an new csv file?", TITLE + ' - save csv file', wx.NO | wx.YES | wx.ICON_WARNING)
                if dlg.ShowModal() != wx.ID_YES: return
                os.remove(csvpath)    # remove old csv file
        dlg.Destroy()
        self.config['Misc']['csvname'] = csvpath
        self.sb.setLed(YELLOW)
        outfile = file(csvpath, 'wt')
        writer = csvwriter.UnicodeCsvWriter(outfile)
        itemcount = self.listwin.GetItemCount()
        for index in xrange(itemcount):
            item = self.listwin.GetItem(index, 0)        
            row = [item.GetText()]
            item = self.listwin.GetItem(index, 1)        
            row.append(item.GetText())
            item = self.listwin.GetItem(index, 2)        
            row.append(item.GetText().replace('.', ''))
            item = self.listwin.GetItem(index, 3)             
            row.append(item.GetText())
            writer.writerow(row)
        outfile.close()
        self.writeToLogwin("Export - %i records written to file '%s'" % (itemcount, csvpath))
        self.sb.setLed(GREEN)
    #-------------------------------------------------------------------------------------------

    def onPrint(self, event):
        if cherryisthere:
            self.writeToLogwin('Print - preparing page(s)')
            template = renderTemplate(file = os.path.join(APPPATH, 'stomaheader.html')) 

            itemcount = self.listwin.GetItemCount()
            for index in xrange(itemcount):
                directory = self.listwin.GetItem(index, 0).GetText()        
                filename = self.listwin.GetItem(index, 1).GetText()        
                filesize = self.listwin.GetItem(index, 2).GetText()
                filedate = self.listwin.GetItem(index, 3).GetText()             
                template += renderTemplate(file = os.path.join(APPPATH, 'stomatemplate.html'))

            template += renderTemplate(file = os.path.join(APPPATH, 'stomafooter.html'))
            printData = self.printhtml.GetPrintData()
            printData.SetOrientation(wx.LANDSCAPE)
            self.printhtml.PageSetup()
            self.printhtml.SetHeader(VERSION)
            self.printhtml.SetFooter('Page @PAGENUM@ of @PAGESCNT@')
            self.printhtml.PreviewText(template)
        else:
            dlg = wx.MessageDialog(self, "Printing is impossible. Please install\nthe 'cherrytemplate' module first.", TITLE + ' - Print matches', wx.OK | wx.ICON_ERROR)
            dlg.ShowModal()
    #-------------------------------------------------------------------------------------------

    def onShowDbInfo(self, event):
        """  """
        win = stomapopup.DbInfoPopup(self.dbwin, self.config, event.GetIndex())
        pos = self.dbwin.ClientToScreen((0,0))  
        sz = self.dbwin.GetSize()               
        win.Position((pos[0] + sz[0] / 2, pos[1] - sz[1] / 2), (0, sz[1]))       
        win.Popup()
    #-------------------------------------------------------------------------------------------

    def onShowJobOrTaskInfo(self, event):
        ''' click in jobwin '''
        item = event.GetItem()
        if item:
            label = self.jobwin.GetItemText(item)
            if self.jobwin.GetItemParent(item) == self.jobwin.GetRootItem():
                jobnames = self.config['Jobs'].keys()
                jobnames.sort()       
                win = stomapopup.JobInfoPopup(self.jobwin, self.config, label)
            else:
                tasknames = self.config['Tasks'].keys()
                tasknames.sort()
                win = stomapopup.TaskInfoPopup(self.jobwin, self.config, label)
            
            pos = self.jobwin.ClientToScreen((0,0))  # pos is the left top edge of the Backup task win
            sz = self.jobwin.GetSize()               # sz is the size of the Backup task win
            win.Position((pos[0] + sz[0] / 2, pos[1] - sz[1] / 2), (0, sz[1]))       
            win.Popup()
                
    #-------------------------------------------------------------------------------------------

    def onManageTasks(self, event):
        dlg = stomatasksman.TasksManDialog(self, self.updateJobwin, self.writeToLogwin, self.config, self.jobtimer)
        dlg.ShowModal()
        dlg.Destroy()
    #-------------------------------------------------------------------------------------------

    def onEditJobOrTask(self, event):
        ''' double click on item in jobwin '''
        item = event.GetItem()
        if item:
            label = self.jobwin.GetItemText(item)
            if self.jobwin.GetItemParent(item) == self.jobwin.GetRootItem():    # job node
                dlg = stomajobsman.EditJobDialog(self, self.updateJobwin, self.config, self.jobtimer, label)
                if dlg.ShowModal() == wx.ID_OK:
                    job, jobname = dlg.getValues()
                    self.config['Jobs'][jobname] = job
                dlg.Destroy()
#                self.updateJobwin()
            else:                                                               # task node
                dlg = stomatasksman.EditTaskDialog(self, self.config, self.jobtimer, label)
                if dlg.ShowModal() == wx.ID_OK:
                    task, taskname = dlg.getValues()
                    self.config['Tasks'][taskname] = task
                dlg.Destroy()
    #-------------------------------------------------------------------------------------------

    def onManageJobs(self, event):
        dlg = stomajobsman.JobsManDialog(self, self.updateJobwin, self.writeToLogwin, self.config, self.jobtimer)
        dlg.ShowModal()
        dlg.Destroy()
        self.updateJobwin()
    #-------------------------------------------------------------------------------------------

    def onToggleImmediate(self, event = None):
        if event != None:
            item = event.GetItem()
            if item:
                label = self.jobwin.GetItemText(item)
                if self.jobwin.GetItemParent(item) == self.jobwin.GetRootItem():
                    jobdata = self.config['Jobs'][label]
                    jobdata['immediate'] = str(not (jobdata['immediate'] == 'True'))
        
        self.updateJobwin(True)
    #-------------------------------------------------------------------------------------------

    def onImmediateExecution(self, event):
        dirty = False
        jobcount = 0
        jobnames = self.config['Jobs'].keys()
        if len(jobnames) > 0:
            self.toggleAbortAction(True)
            self.jobisrunning = True
            for jobname in jobnames:
                jobdata = self.config['Jobs'][jobname]
                if jobdata['immediate'] == 'True':
                    jobcount += 1
                    self.writeToLogwin("Immediate job exec - executing job '%s'" % (jobname))
                    count = 0
                    for taskname in jobdata['tasklist']:
                        if  not self.abortaction:
                            taskdata = self.config['Tasks'][taskname]
                            if not os.path.exists(taskdata['sourcepath']):
                                dlg = wx.MessageDialog(self, "The sourcepath '%s'\ndoes not exist! Task aborted." % \
                                                           (taskdata['sourcepath']), TITLE + ' - execute backup task',
                                                           wx.OK | wx.ICON_ERROR)
                                dlg.ShowModal()
                                self.writeToLogwin("Immediate job exec - executing job '%s' stopped due to errors" % (jobname))
                                continue
                            if not os.path.exists(taskdata['targetpath']):
                                dlg = wx.MessageDialog(self, "The targetpath '%s'\ndoes not exist! Task aborted." % \
                                                           (taskdata['targetpath']), TITLE + ' - execute backup task',
                                                           wx.OK | wx.ICON_ERROR)
                                dlg.ShowModal()
                                self.writeToLogwin("Immediate job exec - executing job '%s' stopped due to errors" % (jobname))
                                continue                  
                            if taskdata['override'] == 'False' and (taskdata['timestamp'] == 'False' or taskdata['comprmeth'] == 'no compression'):
                                dirlist = os.listdir(taskdata['targetpath'])
                                if len(dirlist):
                                    dlg = wx.MessageDialog(self, "The target directory '%s'\n is not empty! You possibly may override (part of) it's contents! Is this, what you really want to do?" % \
                                                           (taskdata['targetpath']), TITLE + ' - execute backup task',
                                                           wx.YES_NO | wx.ICON_WARNING)
                                    if dlg.ShowModal() != wx.ID_YES:
                                        self.writeToLogwin("Immediate job exec - executing job '%s' stopped due to errors" % (jobname))
                                        continue
                            self.sb.setLed(LBLUE)
                            self.writeToLogwin("Immediate job exec - executing task '%s'" % (taskname))
                            wx.SafeYield(self)
                            try:
                                be_task = stomabkupexec.BackupExec(self.getAbortAction, taskname, self.stomadb,
                                                                   self.config, self.sb.SetStatusText, self.updateBackupFiles)
                                filecount, skipcount, errorcount, dbdirty = be_task.executeTask()
                                dirty = dirty or dbdirty
                                self.writeToLogwin("Immediate job exec - task '%s' executed, %d files, %d skipped, %d errors" % (taskname, filecount, skipcount, errorcount))
                                taskdata['lastrun'] = time.strftime('%Y-%m-%d %H:%M')
                                count += 1
                            except:
                                self.writeToLogwin("Immediate job exec - unknown error while executing task '%s'" % (taskname))
                    self.writeToLogwin('Immediate job exec - total of %d task(s) successfully executed' % (count))        
                    jobdata['lastrun'] = jobdata['nextrun'] = time.strftime('%Y-%m-%d %H:%M')
                
            if self.stomadb != None and self.dbiswritable:
                self.stomadb.commitTransaction()
            elif self.config['Database']['autoupd'] == 'True':
                self.writeToLogwin('Immediate job exec - database not updated (no db connection or db not writable)', RED)                                        
            self.updateDbName()
            if self.abortaction:
                self.writeToLogwin('Immediate job exec - execution aborted by user', RED)
            self.jobisrunning = False
            self.toggleAbortAction(False)
            if dirty:
                dlg = wx.MessageDialog(self, "One or more files in the target directory have been overwitten. The database is currently NOT synchronized. You possibly may want to init the database and rescan the target directories.",
                                       TITLE + ' - execute backup task', wx.OK | wx.ICON_INFORMATION)
                dlg.ShowModal()
        self.writeToLogwin('Immediate job exec - total of %d job(s) successfully executed' % (jobcount))
    #-------------------------------------------------------------------------------------------
    
    def onJobTimer(self, event):
        if self.config['Execution']['autoexec'] == 'False' or self.jobisrunning: return

        dirty = False
        jobcount = 0
        jobnames = self.config['Jobs'].keys()
        t = time.time()
        now = time.strftime('%Y-%m-%d %H:%M', time.localtime(t))

        for jobname in jobnames:
            jobdata = self.config['Jobs'][jobname]
            if jobdata['background'] == 'False': continue
            
            self.jobisrunning = True
            self.toggleAbortAction(True)        
            self.sb.setLed(LBLUE)
            wx.SafeYield(self)
            
            if (jobdata['delayed'] == 'True' and int(jobdata['interval']) and jobdata['nextrun'] < now) \
                or (jobdata['delayed'] == 'False' and jobdata['nextrun'] == now):
                jobcount += 1
                self.writeToLogwin("Unattended job exec - executing job '%s' at %s" % (jobname, now))
                count = 0
                wx.SafeYield(self)
                for taskname in jobdata['tasklist']:
                    if  not self.abortaction:
                        taskdata = self.config['Tasks'][taskname]
                        if not os.path.exists(taskdata['sourcepath']):
                            self.writeToLogwin('Unattended job exec - abort (sourcepath does not exist)')
                            break
                        if not os.path.exists(taskdata['targetpath']):
                            self.writeToLogwin('Unattended job exec - abort (targetpath does not exist)')
                            break                  
                        if taskdata['override'] == 'False' and (taskdata['timestamp'] == 'False' or taskdata['comprmeth'] == 'no compression'):
                            dirlist = os.listdir(taskdata['targetpath'])
                            if len(dirlist):
                                self.writeToLogwin('Unattended job exec - abort (targetpath not empty)')
                                break
                        self.writeToLogwin("Unattended job exec - executing task '%s'" % (taskname))
                        wx.SafeYield(self)
                        try:
                            be_task = stomabkupexec.BackupExec(self.getAbortAction, taskname, self.stomadb,
                                                               self.config, self.sb.SetStatusText, self.updateBackupFiles)
                            filecount, skipcount, errorcount, dbdirty = be_task.executeTask()
                            dirty = dirty or dbdirty
                            self.writeToLogwin("Unattended job exec - task '%s' executed, %d files, %d skipped, %d errors" % (taskname, filecount, skipcount, errorcount))
                            taskdata['lastrun'] = time.strftime('%Y-%m-%d %H:%M')
                            count += 1
                        except:
                            self.writeToLogwin("Unattended job exec - unknown error while executing task '%s'" % (taskname))
                self.writeToLogwin('Unattended job exec - total of %d tasks executed' % (count))        
                jobdata['nextrun'] = time.strftime('%Y-%m-%d %H:%M',               
                                                   time.localtime(t + int(jobdata['interval']) * 86400))                
                jobdata['lastrun'] = now
                
        if self.stomadb != None and self.dbiswritable:
            self.stomadb.commitTransaction()
        elif self.config['Database']['autoupd'] == 'True':
            self.writeToLogwin('Unattended job exec - database not updated (no db connection or db not writable)', RED)                                        
            
        # we have to wait if all jobs execute in less than 30 sec
        while not self.abortaction and jobcount > 0 and time() < t + JOB_TIME_SLICE / 1000: 
            wx.SafeYield(self)
            
        if jobcount:
            self.writeToLogwin('Unattended job exec - total of %d job(s) executed' % (jobcount))            
        self.updateDbName()
        if self.abortaction:
            self.writeToLogwin('Unattended job execution - execution aborted by user', RED)
        self.jobisrunning = False
        self.toggleAbortAction(False)
        if dirty:
            self.writeToLogwin("Unattended job exec - one or more files in the target directory have been overwitten. The database is currently NOT synchronized. You possibly may want to init the database and rescan the target directories.")                                         
    #-------------------------------------------------------------------------------------------

    def onAbout(self, event):
        dlg = stomaabout.AboutDialog(self)
        dlg.ShowModal()
    #-------------------------------------------------------------------------------------------

    def handleConfigData(self, rw = 'r'):
        if rw == 'r':                              # read  / check ini file
            inipath = HOMEDIR
            if len(sys.argv) > 1:
                argument = sys.argv[1].lower()
                if argument.startswith('-i='):
                    inipath = sys.argv[1][3:]
            self.inifilename = os.path.join(inipath, TITLE + '.ini')
            self.config = ConfigObj(self.inifilename, encoding = 'UTF8')
            if not self.config.has_key('Version'):              # new, empty ini file
                # initialize all values
                self.config.initial_comment = ['StoreMan preferences file']
                self.config['Version'] = {}
                self.config['Version']['vernum'] = VERNUM
                self.config['Window'] = {}
                self.config['Window']['posx'] = 0
                self.config['Window']['posy'] = 0
                self.config['Window']['sizex'] = 800
                self.config['Window']['sizey'] = 600
                self.config['Window']['colwidth0'] = 390
                self.config['Window']['colwidth1'] = 188
                self.config['Window']['colwidth2'] = 83
                self.config['Window']['colwidth3'] = 122
                self.config['Window']['perspective'] = ''
                self.config['Window']['tcexpstate'] = []
                self.config['View'] = {}                
                self.config['View']['scanwin'] = 'True'
                self.config['View']['histwin'] = 'True'
                # self.config['View']['taskwin'] = 'True' deleted since 1.23
                self.config['View']['jobwin'] = 'True'
                self.config['View']['logwin'] = 'True'
                self.config['Query'] = {}
                self.config['Query']['dirmask'] = ['%']
                self.config['Query']['filemask'] = ['%']
                self.config['Query']['compare'] = '>='
                self.config['Query']['sizemask'] = 0
                self.config['Query']['datemask'] = ['%']
                self.config['Scan'] = {}
                self.config['Scan']['scandirs'] = []        # [os.path.normpath(HOMEDIR)]
                self.config['Scan']['history'] = []
                self.config['Scan']['lasttarget'] = os.sep
                self.config['Database'] = {}
                self.config['Database']['index'] = 0
                self.config['Database']['dbhistory'] = [os.path.normpath(HOMEDIR + '/default.db3')]
                self.config['Database']['limit'] = 5000
                self.config['Database']['ignorecase'] = 'True'
                self.config['Database']['orderby'] = 'filename'
                self.config['Database']['autoupd'] = 'True'
                self.config['ListCtrl'] = {}
                self.config['ListCtrl']['grid'] = 'True'
                self.config['ListCtrl']['font'] = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT).GetFaceName()
                self.config['ListCtrl']['fsize'] = '9'
                self.config['ListCtrl']['fileman'] = ['krusader --left %dir', 'dolphin %dir', 
                                                      'konqueror --select %dir', 'nautilus -n %dir',
                                                      'explorer.exe %dir']                
                self.config['ListCtrl']['confirm'] = 'True'
                self.config['Execution'] = {}
                self.config['Execution']['startmin'] = 'False'
                self.config['Execution']['autoexec'] = 'False'
                self.config['Execution']['offset'] = '180'
                self.config['Jobs'] = {}
                self.config['Tasks'] = {}
                self.config['Misc'] = {}
                self.config['Misc']['hlcolor'] = (194, 226, 194)
                self.config['Misc']['update'] = 'True'
                self.config['Misc']['priority'] = 'False'
                self.config['Misc']['hiddendirs'] = 'False'
                self.config['Misc']['errorlog'] = 'False'
                self.config['Misc']['actionlog'] = 'False' 
                self.config['Misc']['csvname'] = os.path.normpath(HOMEDIR + '/StoreMan.csv')
                self.config['Misc']['unit'] = 'KB'
            elif self.config['Version']['vernum'][:3] != VERNUM[:3]:    # incompatible version
                if self.config['Version']['vernum'].startswith('1.2'):  
                    self.config['Version']['vernum'] = VERNUM           # convert 1.2 settings to 1.3
                    if not self.config['Misc'].has_key('actionlog'):
                        self.config['Misc']['actionlog'] = 'False'      # added in Version 1.24   
                    if not self.config['Misc'].has_key('update'):
                        self.config['Misc']['update'] = 'True'          # added in Version 1.26   
                if self.config['Version']['vernum'].startswith('1.3'):  # convert 1.3 settings to 1.4
                    self.config['Version']['vernum'] = VERNUM
                    # other changes go here
            else:                                                       # and so on
                pass                                                    # may be in Version > 1.30
        else:                                   # write ini file
            self.config['Version']['vernum'] = VERNUM
            windowrect = self.GetRect()
            self.config['Window']['posx'] = windowrect[0]
            self.config['Window']['posy'] = windowrect[1]
            self.config['Window']['sizex'] = windowrect[2]
            self.config['Window']['sizey'] = windowrect[3]
            self.config['Window']['colwidth0'] = self.listwin.GetColumnWidth(0)
            self.config['Window']['colwidth1'] = self.listwin.GetColumnWidth(1)
            self.config['Window']['colwidth2'] = self.listwin.GetColumnWidth(2)
            self.config['Window']['colwidth3'] = self.listwin.GetColumnWidth(3)
            self.config['Window']['perspective'] = self._mgr.SavePerspective()
            self.config['Window']['tcexpstate'] = self.jobwin.GetExpansionState()
            self.config.write()
    #-------------------------------------------------------------------------------------------

    def onHandbook(self, event):
        """ show Html help """
        webbrowser.open(os.path.join(APPPATH, 'storeman.html'))
    #-------------------------------------------------------------------------------------------

    def onSysTray(self, event):
        self.tbicon.minimizeToTaskbar()
    #-------------------------------------------------------------------------------------------

    def OnRestoreWindow(self, event):
        self.Show(True)
        self.Iconize(False)
        self.tbicon.RemoveIcon()
        self.Raise()       
    #-------------------------------------------------------------------------------------------------------
    
    def toggleAbortAction(self, togglestate):
        self.abortaction = not togglestate
        self.mainmenu.Enable(self.ID_ABORT_ACTION, togglestate)
        self.tb.EnableTool(self.ID_ABORT_ACTION, togglestate)
    #-------------------------------------------------------------------------------------------------------

    def getAbortAction(self):
        return self.abortaction
    #-------------------------------------------------------------------------------------------------------

    def onAbortAction(self, event):
        self.abortaction = True
        self.writeToLogwin('Abort - current action abort requested by user', RED)
    #-------------------------------------------------------------------------------------------------------

    def onChar(self, event):
        keycode = event.GetKeyCode()
        print keycode
        if keycode == 43:           # '+'
            self.jobwin.ExpandAll()
        elif keycode == 45:         # '-'
            self.jobwin.CollapseAll()
        event.Skip()
    #-------------------------------------------------------------------------------------------------------
    
    def onCloseWindow(self, event = None):
        mountcount = len(self.config['Misc']['mountedbysm'])
        if mountcount:
            dlg = wx.MessageDialog(self, 'you have %d StoreMan mounted file system left.\nGo back to the (Un)Mount fs dialog now?' % \
                                  (mountcount), TITLE + ' - Manage mount fs', wx.YES_NO | wx.ICON_INFORMATION)
            if dlg.ShowModal() == wx.ID_YES:
                dlg.Destroy()
                mmdlg = stomamountman.MountManDialog(self, self.config, self.writeToLogwin)
                mmdlg.ShowModal()
                mmdlg.Destroy()
                return
            
        self.handleConfigData('w')
        self.logger.info('StoreMan shutdown\n')
        logging.shutdown()
        self._mgr.UnInit()          # deinitialize the frame manager
        self.Destroy()              # switch the light off
# - class StoreManFrame(wx.Frame):----------------------------------------------------------------

class StoreManApp(wx.App):
    def OnInit(self):
        frame = StoreManFrame(None, -1, VERSION)
        frame.Show(True)
        return True
# - class StoreManApp(wx.App):--------------------------------------------------------------------
