#!/usr/bin/env python3

"""
v 8.0

kAnyRemote
KDE GUI for anyRemote - a bluetooth remote for your PC.

Copyright (C) 2007-2019 Mikhail Fedotov <anyremote@mail.ru>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty ofF
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, write to the Free Software Foundation, 
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 
"""

import locale
import gettext
import getopt
import glob
import os
import re
import signal
import socket
import threading
import _thread
import http.client
import time
import sys
import datetime

#Translation stuff
#print sys.executable

#Get the local directory since we are not installing anything
dirpath = os.path.dirname(sys.argv[0])
local_path = os.path.realpath(os.path.join(dirpath,'mo'))

# try ../share/locale/ (Linux)
if not os.path.exists(local_path):
    local_path = os.path.realpath(os.path.join(dirpath,'../share/locale/'))

# try ../lib/locale/ (Sun)
if not os.path.exists(local_path):
    local_path = os.path.realpath(os.path.join(dirpath,'../lib/locale/'))

# last resort ... try /usr/share/locale
if not os.path.exists(local_path):
    local_path = os.path.realpath('/usr/share/locale/')

langs = []

lc = ''
encoding = ''

try:
    lc, encoding = locale.getdefaultlocale()
except ValueError as cmd:
    print('Error: ',cmd,'\nCorrect $LANG or $LANGUAGE first !!!')
    sys.exit()

if lc:
    #If we have a default, it's the first in the list
    langs = [lc]


# Now lets get all of the supported languages on the system
language = os.environ.get('LANGUAGE', None)
if language:
    langs += language.split(":")

APP_NAME = "kanyremote"

gettext.bindtextdomain(APP_NAME, local_path)
gettext.textdomain(APP_NAME)

# Get the language to use
lang = gettext.translation(APP_NAME, local_path, languages=langs, fallback = True)

_ = lang.gettext

try:
    from PyQt5.QtWidgets import *
    from PyQt5.QtGui  import *
    from PyQt5.QtCore import *
except ImportError:
   print('Install PyQt5 first !!!')
   sys.exit()

pybluez = True 
try:
    import bluetooth
except ImportError:
   pybluez = False

def _tr(string):
    ob = QObject()
    return ob.tr(_(string))

#
# String constants (not all!)
#
AR_INSTALLED    = _tr('Installed')
AR_NOTINSTALLED = _tr('Not installed')

AR_NOINFO   = _tr('No information')
AR_AVAIL    = _tr('Available')
AR_NOTAVAIL = _tr('Not available')
AR_MANAGED  = _tr('Managed')
AR_RUNNING  = _tr('Running')

AR_CONN_DEV = _tr('Connecting to device')
AR_WAIT_OPS = _tr('Wait other operations to finish')

AR_TCP_PORT=_tr('TCP port')
AR_BT_PORT=_tr('Bluetooth channel')
AR_HTTP_PORT=_tr('HTTP port')


AR_DOWNLOADING=_tr('Downloading')
AR_J2ME_CHECK  = _tr('Checking J2ME client updates')
AR_NO_J2ME_UPD = _tr("No updates for J2ME client were found.")

AR_CODE_J2ME_UPD = 20014
AR_CODE_LOADED   = 20017

AR_FILE_DEVICES='anyremote-peers.conf'

def sigint_handler(signal, frame):
    #print('sigint_handler')
    sendMainWindow(29999,'')      

signal.signal(signal.SIGINT, sigint_handler)
#signal.SIG_DFL)
           
###############################################################################
#
# J2ME client uploader
# unified
#
###############################################################################
class JCUploader(threading.Thread):

    def __init__(self):
        threading.Thread.__init__(self) 

    def run(self):
        global debug,jup
        
        if debug: print('JCUploader starting')
    
        self.run = True
        
        stopPBar()
        startPBar(AR_DOWNLOADING+' anyRemote-16.jad')
         
        ret = True
        
        jars = ["-16","-32","-48","-64","-128","","-nojsr82"]
        
        for sz in jars:
        
            for bi in ["", "b"]:
                
                # no big icons for these variants
                if bi == "b" and (sz == "" or sz == "-nojsr82"):
                    continue
                
                if self.run and ret:
                    ret = uploadJ2MEWebGui('anyRemote'+sz+bi+'.jad', True)

                if self.run and ret: 
                    ret = uploadJ2MEWebGui('anyRemote'+sz+bi+'.jar')
    
        if self.run: stopPBar()
        
        if debug: print('JCUploader: upload done')
        
        jup = None

    def stop(self):
        global debug
        if debug: print('stop JCUploader')
        
        self.run = False

def stopJCUploader():
    global jup
    try:
        if jup != None:
            jup.stop()
    except NameError:
        pass
    time.sleep(0.5)
    jup = None

##################################################################################
#
# J2ME client verifier
# unified
#
##################################################################################
class JCVerifier(threading.Thread):

    def __init__(self, force):
        threading.Thread.__init__(self) 
        self.anyway = force

    def run(self):
        global debug
        
        if debug: print('JCVerifier starting')
        
        curdate = time.strftime("%d%m", time.gmtime())
        
        if self.anyway:
            startPBar(AR_J2ME_CHECK)
        else:
            try:
                f = open(os.environ.get("HOME")+os.sep+'.anyRemote'+os.sep+'j2me-client.checked', 'r')
                date = f.read() 
                f.close()        
                date = date.replace('\n','')
                    
                if debug: print('JCVerifier',date,curdate)
            
                if date == curdate:
                    if debug: print('JCVerifier: already checked. Exiting')
                    self = None
                    return

            except IOError:
                pass
            
        if debug: print('JCVerifier: check new version')

        os.system('echo \"'+curdate+'\" > $HOME/.anyRemote/j2me-client.checked')
        newver = checkJ2MEClientUpdate();
        
        if debug: print('JCVerifier: upload done '+newver)
        
        if self.anyway:    
            stopPBar()
        
        if newver == "":
            if self.anyway:
                infoMessage(AR_NO_J2ME_UPD)
        # newver == "-1" in case of connection absense
        elif newver != "-1": 
            codeMessage(AR_CODE_J2ME_UPD)
        
        self = None

##################################################################################
#
# Reader of cfg. files
# unified
#
##################################################################################
class CfgFileReader(threading.Thread):

    def __init__(self):
        global cfgFileReaderFlag
        
        threading.Thread.__init__(self) 
        
        cfgFileReaderFlag = True

    def run(self):
        global debug, cfgFileReaderFlag, cfgReader

        if debug: print('CfgFileReader.run')
        
        clearAppsList()
        
        self.regExp  = re.compile("^[^a-zA-Z0-9]*Gui")
        self.regExpB  = re.compile("^[^a-zA-Z0-9%]*VERS[^=]*=.+$")
        self.regExpAT = re.compile("^[^a-zA-Z0-9%]*Device[^=]*=/dev/[a-zA-Z0-9]+$")
        
        t1 = datetime.datetime.now()
        
        numProcessed = 0
        for cfgDir in cfgReader.cfgDirs:
              
            if cfgDir == '':
                continue
            if debug: print('Collect files in '+cfgDir)
            
            if os.path.isfile(cfgDir):
                self.processOneFile(cfgDir)
                
                numProcessed = numProcessed+1
                codeMessage2(AR_CODE_LOADED, numProcessed)
                
            else:
                for root, dirs, files in os.walk(cfgDir):
                    
                     
                    if 'Utils' in dirs:
                       dirs.remove('Utils')
                       if debug: print('skip dir',root+os.sep+'Utils')
                    if 'Icons' in dirs:
                       dirs.remove('Icons')
                       if debug: print('skip dir',root+os.sep+'Icons')
                    if '.svn' in dirs:
                       dirs.remove('.svn')
                       if debug: print('skip dir',root+os.sep+'.svn')
                    
                    for cfgFile in files:

                        nameext = cfgFile.split('.')
                        ext = nameext[len(nameext)-1]

                        if ext == 'png' or ext == 'sh' or ext == 'py' or ext == 'gz':
                            if debug: print('Not a configuration file '+cfgFile)
                            continue

                        self.processOneFile(root + os.sep + cfgFile)

                        numProcessed = numProcessed+1
                        codeMessage2(AR_CODE_LOADED, numProcessed)
        
        codeMessage2(AR_CODE_LOADED, -1)
        
        cfgFileReaderFlag = False
        
        t2 = datetime.datetime.now()
        #print("Load time ",(t2-t1))
            
    def processOneFile(self,cfgFile):
        global debug, cfgReader
        
        fd = None
        try:
            fd = open(cfgFile,'r')
        except IOError:
            pass
         
        if fd:
            aName    = None
            aInst    = ''
            aRun     = ''
            aIcon    = ''
            aType    = None
            aDesc    = ''
            aVers    = ''
            aModes   = ''
            aDevice  = ''
            aBemused = ''
        
            try:
                for line in fd:

                    if self.regExp.match(line):

                        p = re.search("^[^a-zA-Z0-9]*GuiAppName[^=]*=(.+)$", line)
                        if p != None:
                            aName = p.group(1)

                        p = re.search("^[^a-zA-Z0-9]*GuiAppBinary[^=]*=(.+)$", line)
                        if p != None:
                            aInst = p.group(1)

                        p = re.search("^[^a-zA-Z0-9]*GuiAppRun[^=]*=(.+)$", line)
                        if p != None:
                            aRun = p.group(1)

                        p = re.search("^[^a-zA-Z0-9]*GuiAppIcon[^=]*=(.+)$", line)
                        if p != None:
                            aIcon = p.group(1)

                        p = re.search("^[^a-zA-Z0-9]*GuiAppType[^=]*=(.+)$", line)
                        if p != None:
                            aType = p.group(1)

                        p = re.search("^[^a-zA-Z0-9]*GuiAppDesc[^=]*=(.+)$", line)
                        if p != None:
                            aDesc = p.group(1)

                        p = re.search("^[^a-zA-Z0-9]*GuiAppVersion[^=]*=(.+)$", line)
                        if p != None:
                            aVers = p.group(1)

                        # deprecated, use GuiAppProtocols        
                        p = re.search("^[^a-zA-Z0-9]*GuiAppModes[^=]*=(.+)$", line)
                        if p != None:
                            aModes = p.group(1)

                        p = re.search("^[^a-zA-Z0-9]*GuiAppProtocols[^=]*=(.+)$", line)
                        if p != None:
                            aModes = p.group(1)

                    if self.regExpAT.match(line):
                        aDevice = 'a'

                    if self.regExpB.match(line):
                        aBemused = 1

            except (UnicodeDecodeError):
               if debug: print('File decode error ',cfgFile)
               fd.close()
               return
        
            if (not aName) and (not aType):
               if debug: print('Not a configuration file. Skip '+cfgFile)
               fd.close()
               return
            
            aMode = ['Server']    
            if aModes == '': 
                if aBemused == 1:
                    aMode = ['Bemused']
                elif aDevice == 'a':
                    aMode = ['AT']
            else:
                aMode = [] 
                # not use aModes.split(',') to make more strict control
                if aModes.find('Server') >= 0:
                    aMode.append('Server')

                if aModes.find('Bemused') >= 0:
                    aMode.append('Bemused')
                
                if aModes.find('AT') >= 0:
                    aMode.append('AT')
                
                if aModes.find('iViewer') >= 0:
                    aMode.append('iViewer')
                    
            if aName == '' or aName == None:
                    aName = os.path.basename(cfgFile)
                    
            app = aInst
            isInst = 'OK'
            if aInst != '':
                cnt = aInst.count(' ')
                
                if cnt > 0: # treat this like a command to execute
                    app = getResult(aInst, 'bin')
                
                isInst = isInstalled(app)

            show  = 1
            show1 = 1
            show2 = 1
            status = AR_NOTAVAIL
            if isInst == 'OK':
                status = AR_AVAIL
                
                # check version if requested
                if aVers != '':
                    version = reqVersion(aVers)

                    if version == 'NOK':
                        isInst = 'NOK'
                        status = AR_NOTAVAIL
            
            if isInst == 'NOK' and not cfgReader.showNonavail_:
                show1 = 0
            elif isInst == AR_NOINFO:
                status = ''

            if 'Server' in aMode and not cfgReader.showSrv_:
                aMode.remove('Server')
            
            if 'AT' in aMode and not cfgReader.showAt_:
                aMode.remove('AT')
             
            if 'Bemused' in aMode and not cfgReader.showBm_:
                aMode.remove('Bemused')
        
            if 'iViewer' in aMode and not cfgReader.showIView_:
                aMode.remove('iViewer')
                    
            if len(aMode) > 0:
                show = 1
            else:
                show = 0

            if aType == 'Application' and not cfgReader.showApps_:
                show2 = 0
            elif aType == 'Custom' and not cfgReader.showCustom_:
                show2 = 0
            elif aType == 'Example' and not cfgReader.showExamples_:
                show2 = 0
            elif aType == '':
                show2 = 0
            
            if show == 1 and show1 == 1 and show2 == 1:
                if debug: print('Proceed '+aName)
                                    
                isRun = AR_NOINFO
                if aRun != '':
                    isRun  = getResult(aRun,'reader')
                
                if status == AR_AVAIL and isRun == 'OK':
                    status = AR_RUNNING
                 
                addAppToList(aName, aIcon, app, aRun, isRun, cfgFile, status, aMode, aType, aDesc)
                
                if debug: print('Processed',cfgFile)
            else:
                if debug: print('Skip',cfgFile)
                    
            fd.close()

###############################################################################        
#
# Configuration management
# unified
#
###############################################################################        

class ConfigReader:
    def saveConfig(self):
        
        cfg = os.environ.get("HOME")+os.sep+'.anyRemote'
        if not os.path.exists(cfg):
            os.mkdir(cfg)
            
        f=open(cfg + os.sep + 'anyremote-fe.conf', 'w')
        if f:
            f.write('LastCfgFile='+self.lastCfgFile_+'\n')
            
            what = 'Show='
            if self.showApps_:
                what = what + 'Apps,'
            if self.showCustom_:
                what = what + 'Custom,'
            if self.showExamples_:
                what = what + 'Examples,'
            if self.showNonavail_:
                what = what + 'NonAvail,'
            if self.showAt_:
                what = what + 'AT,'
            if self.showSrv_:
                what = what + 'Server,'
            if self.showBm_:
                what = what + 'Bemused,'
            if self.showIView_:
                what = what + 'IViewer,'
            
            f.write(what+'\n')

            ac = '0'
            if self.autoReconn_:
                ac = '1'
            f.write('AutoReconnectAT='+ac+'\n')
            
            if self.dvSrvBT_ != '':
                f.write('DeviceServerBT='+self.dvSrvBT_+'\n')
            
            if self.dvSrvTCP_ != '':
                f.write('DeviceServerTCP='+self.dvSrvTCP_+'\n')
            
            if self.dvSrvWEB_ != '':
                f.write('DeviceServerWEB='+self.dvSrvWEB_+'\n')
            
            if self.dvSrvADV_ != '':
                f.write('DeviceServerManual='+self.dvSrvADV_+'\n')
             
            useTypes = 'DeviceUseType='
            if self.dvSrvUseADV_:
                useTypes = useTypes + 'ADV'
            else:
                if self.dvSrvUseBT_:
                    useTypes = useTypes + 'BT,'
                if self.dvSrvUseTCP_:
                    useTypes = useTypes + 'TCP,'
                if self.dvSrvUseWEB_:
                    useTypes = useTypes + 'WEB,'
                if self.dvSrvUseAVH_:
                    useTypes = useTypes + 'Avahi,'
            f.write(useTypes+'\n')

            if self.deviceAT_ != '':
                f.write('DeviceAT='+self.deviceAT_+'\n')

            if self.deviceBm_ != '':
                f.write('DeviceBemused='+self.deviceBm_+'\n')

            if self.deviceIV_ != '':
                f.write('DeviceIViewer='+self.deviceIV_+'\n')

            tm = "%s" % (self.updateTmout_)
            f.write('UpdateTimeout='+tm+'\n')
                
            if self.autoStart_ != '':
                f.write('AutoStartFile='+self.autoStart_+'\n')

            if self.doAutoStart_:
                f.write('AutoStart=true\n')
            else:
                f.write('AutoStart=false\n') 

            dirs = 'CfgDirs='
            for d in self.cfgDirs:
                if d != '':
                    dirs = dirs +d + ';'
            f.write(dirs+'\n')
            
            if self.browseTmout_ > 0:
                tm = "%s" % (self.browseTmout_)
                f.write('DeviceBrowserTimeout='+tm+'\n')
            else:
                f.write('DeviceBrowserTimeout=-1\n')
           
            f.write('J2MEDir='+self.j2meDir_+'\n')
            
            if self.checkJ2MEUpdate_:
                f.write('CheckJ2MEUpdate=true\n')
            
            f.close()
            
        saveDevices()

    def setDefaultConfig(self):

        self.lastCfgFile_  = ''
        self.cfgDirs       = []
        self.autoStart_    = ''
        self.doAutoStart_  = False
        self.j2meDir_      = ''
        self.checkJ2MEUpdate_ = False
        self.showApps_     = True
        self.showCustom_   = False
        self.showExamples_ = False
        self.showNonavail_ = False
        self.showAt_       = False
        self.showSrv_      = True
        self.showBm_       = False
        self.showIView_    = False
        self.autoReconn_   = False
        self.dvSrvBT_      = '19'
        self.dvSrvTCP_     = '5197'
        self.dvSrvWEB_     = '5080'
        self.dvSrvADV_     = 'bluetooth:19 -log'
        self.dvSrvUseBT_   = True
        self.dvSrvUseTCP_  = True
        self.dvSrvUseWEB_  = True
        self.dvSrvUseAVH_  = True
        self.dvSrvUseADV_  = False
        self.deviceAT_     = 'rfcomm:'
        self.deviceBm_     = ''
        self.deviceIV_     = 'tcp:5197,web:5198'

        self.updateTmout_  = 60
        self.browseTmout_  = -1
        
    def readConfig(self):
        
        # anyRemote related part
        cfg  = os.environ.get("HOME")+os.sep+'.anyRemote' + os.sep + 'anyremote-fe.conf'
        
        if os.path.exists(cfg):
            f=open(cfg, 'r')
            if f:
                for line in f:
                    line = line.replace('\n','')
 
                    if line.startswith('LastCfgFile='):
                        self.lastCfgFile_=line[12:]
                    elif line.startswith('Show='):
                        #Show=Apps,Custom,Examples
                        what = line[5:].split(',')

                        # override defaults
                        self.showApps_ = False
                        self.showSrv_  = False
                        
                        for item in what:
                            if item == 'Apps':
                                self.showApps_ = True
                            elif item == 'Custom':
                                self.showCustom_ = True
                            elif item == 'Examples':
                                self.showExamples_ = True
                            elif item == 'NonAvail':
                                self.showNonavail_ = True
                            elif item == 'AT':
                                self.showAt_ = True
                            elif item == 'Server':
                                self.showSrv_ = True
                            elif item == 'Bemused':
                                self.showBm_ = True
                            elif item == 'IViewer':
                                self.showIView_ = True
                          
                            # compatibility with old versions
                            elif item == 'All':
                                self.showSrv_      = True
                                self.showApps_     = True
                                self.showCustom_   = True
                                self.showExamples_ = True
                                self.showNonavail_ = True
                              
                    elif line.startswith('AutoReconnectAT='):
                        self.autoReconn_ = (line == 'AutoReconnectAT=1')
                    elif line.startswith('AutoStart='):
                        useM = line[10:]
                        if useM == '1' or useM == 'true' or useM == 'True':
                            self.doAutoStart_ = True
                        else:
                            self.doAutoStart_ = False
                    elif line.startswith('AutoStartFile='):
                        self.autoStart_ = line[14:]
                    elif line.startswith('DeviceServerBT='):
                        self.dvSrvBT_ = line[15:]
                    elif line.startswith('DeviceServerTCP='):
                        self.dvSrvTCP_ = line[16:]
                    elif line.startswith('DeviceServerWEB='):
                        self.dvSrvWEB_ = line[16:]
                    elif line.startswith('DeviceServerManual='):
                        self.dvSrvADV_ = line[19:]
                    elif line.startswith('DeviceUseType='):
                        
                        self.dvSrvUseBT_  = False
                        self.dvSrvUseTCP_ = False
                        self.dvSrvUseWEB_ = False
                        self.dvSrvUseAVH_ = False
                        self.dvSrvUseADV_ = False

                        what = line[14:].split(',')
                        
                        for item in what:
                            if item == 'BT':
                                self.dvSrvUseBT_  = True
                                self.dvSrvUseADV_ = False
                            elif item == 'TCP':
                                self.dvSrvUseTCP_ = True
                                self.dvSrvUseADV_ = False
                            elif item == 'WEB':
                                self.dvSrvUseWEB_ = True
                                self.dvSrvUseADV_ = False
                            elif item == 'Avahi':
                                self.dvSrvUseAVH_ = True
                            elif item == 'ADV':
                                self.dvSrvUseBT_  = False
                                self.dvSrvUseTCP_ = False
                                self.dvSrvUseWEB_ = False
                                self.dvSrvUseAVH_ = False
                                self.dvSrvUseADV_ = True
                            
                    elif line.startswith('DeviceAT='):
                        self.deviceAT_ = line[9:]
                    elif line.startswith('DeviceBemused='):
                        self.deviceBm_ = line[14:]
                    elif line.startswith('DeviceIViewer='):
                        self.deviceIV_ = line[14:]
                    elif line.startswith('CfgDirs='):
                        self.cfgDirs=line[8:].split(';')
                    elif line.startswith('UpdateTimeout='):
                        self.updateTmout_=int(line[14:])
                    elif line.startswith('DeviceBrowserTimeout='):
                        tmt=line[21:]
                        try:
                            self.browseTmout_=int(tmt)
                        except ValueError:
                            self.browseTmout_=-1

                        if self.browseTmout_ <= 0:
                            self.browseTmout_=-1
                    elif line.startswith('J2MEDir='):
                        self.j2meDir_=line[8:]
                    elif line.startswith('CheckJ2MEUpdate=true'):
                        self.checkJ2MEUpdate_=True
                        
                    # compatibility
                    elif line.startswith('JavaDir='):
                        self.j2meDir_=line[8:]
                    # compatibility
                    elif line.startswith('CheckJavaUpdate=true'):
                        self.checkJ2MEUpdate_=True

                f.close()
		
		# override
                if self.doAutoStart_ and self.autoStart_ != '':
                    self.lastCfgFile_ = self.autoStart_
		    
		# override
                if self.doAutoStart_ and self.autoStart_ != '':
                    self.lastCfgFile_ = self.autoStart_

                # Try to search again
                if self.j2meDir_ == '':
                    self.j2meDir_ = getJ2MEPath()
                
        # device browser related part
        cfg  = os.environ.get("HOME")+os.sep+'.anyRemote' + os.sep + AR_FILE_DEVICES
        
        if os.path.exists(cfg) :
            f=open(cfg, 'r')
            if f:
                for line in f:
                    line = line.replace('\n','')
                    os.path.exists(cfg)
                    
                    if line.startswith('Device='):
                        deviceData=line[7:].split(',')
                        
                        # try to be backward compatible
                        if len(deviceData) < 5:
                            print('Skip peer',deviceData[0],'because of wrong format')
                            continue

                        v1 = deviceData[0]
                        v2 = deviceData[1]
                        v3 = deviceData[2]
                        v4 = deviceData[3]
                        v5 = deviceData[4]
                        
                        addDevice(v1,v2,v3,v4,v5)
                f.close()
                
    def getConnectString(self):
        if self.dvSrvUseADV_:
            return self.dvSrvADV_
        else:
            connStr = ''

            if self.dvSrvUseTCP_ and self.dvSrvTCP_ != '':
                connStr = "tcp:"+self.dvSrvTCP_
            
            sep = ''
            if connStr != '':
                sep = ','

            if self.dvSrvUseBT_ and self.dvSrvBT_ != '':
                connStr = connStr + sep + "bluetooth:"+self.dvSrvBT_

            if connStr != '':
                sep = ','
            
            if self.dvSrvUseWEB_ and self.dvSrvWEB_ != '':
                connStr = connStr + sep +  "web:"+self.dvSrvWEB_

            if connStr != '':
                sep = ','
            
            if self.dvSrvUseAVH_:
                connStr = connStr + sep +  'avahi'
        
            return connStr
        
    def getConnectStatusString(self):
      if cfgReader.showAt_:
        return '';
      if cfgReader.showBm_:
        return '';
      if cfgReader.showIView_:
        temp = cfgReader.deviceIV_;
        temp = temp.replace('web:',AR_HTTP_PORT+' ');
        temp = temp.replace('tcp:',AR_TCP_PORT+' ');
        return temp;
      if self.dvSrvUseADV_:
        return ''
      else:
        connStr = ''
        if self.dvSrvUseTCP_ and self.dvSrvTCP_ != '':
          connStr = AR_TCP_PORT+' '+self.dvSrvTCP_
        sep = ''
        if connStr != '':
          sep = ', '
        if self.dvSrvUseBT_ and self.dvSrvBT_ != '':
          connStr = connStr + sep + AR_BT_PORT+' '+self.dvSrvBT_
        if connStr != '':
          sep = ', '
        if self.dvSrvUseWEB_ and self.dvSrvWEB_ != '':
          connStr = connStr + sep + AR_HTTP_PORT+' '+self.dvSrvWEB_
        if connStr != '':
          sep = ', '
        if self.dvSrvUseAVH_:
          connStr = connStr + sep + 'Avahi'
          return connStr

    def setAutoStartFile(self):

        for cfgDir in self.cfgDirs:
        
            if cfgDir == '':
                continue
        
            if os.path.isfile(cfgDir):
                if cfgDir.find('mediacenter.cfg') >= 0:
                    self.autoStart_ = root + os.sep + cfgFile
                    self.doAutoStart_ = True
                    return 
            else:
                for root, dirs, files in os.walk(cfgDir):
                    if 'Utils' in dirs:
                        dirs.remove('Utils')
                    if 'Icons' in dirs:
                        dirs.remove('Icons')
                    if '.svn' in dirs:
                        dirs.remove('.svn')
            
                    for cfgFile in files:
                        nameext = cfgFile.split('.')
                        ext = nameext[len(nameext)-1]
                        
                        if ext == 'png' or ext == 'sh' or ext == 'py' or ext == 'gz':
                            continue
                        
                        if cfgFile == 'mediacenter.cfg':
                            self.autoStart_ = root + os.sep + cfgFile
                            self.doAutoStart_ = True
                            return 

##################################################################################
#
# Application status updater
#
##################################################################################
class StatusUpdater(threading.Thread):

    def __init__(self, timeout):
        threading.Thread.__init__(self) 
        self.tmout = timeout

    def run(self):
        global debug, appData
        
        if debug: print('StatusUpdater.run',self.tmout)
        self.runUpdater = True
        
        while self.runUpdater:
            timer = 0
            
            # catch stop event without waiting full timeout
            while self.runUpdater and timer < self.tmout:
                time.sleep(0.1)
                timer = timer + 0.1
            
            if debug: print('StatusUpdater: verify')
            
            for k, v in appData.items():
                if not self.runUpdater:
                    break
                        
                aRun = v[3]
                if aRun != '':
                    isRun  = getResult(aRun,'updater')
                            
                    if isRun != v[4]:
                        v[4] = isRun
                        sendMainWindow(20006, [k,v[6],isRun])                

    def stop(self):
        global debug
        if debug: print('StatusUpdater.stop')
        self.runUpdater = False

##################################################################################
#
# BT communication thread
#
##################################################################################

# Beware:
# Can not run more than one command on BT adapter at once, since it could hungs 

class BtComm(threading.Thread):

    def __init__(self, jDir):
        threading.Thread.__init__(self) 
        self.midletDir = jDir

    def run(self):
        global debug, bt_devices, cmd_array
        
        self.keepRun = True
        
        if debug: print('BtComm: run')
        
        while self.keepRun:
        
            ctype = ''
            cdata = ''

            try:
                item = cmd_array[0]
                del cmd_array[0] 
                ctype = item[0]
                cdata = item[1]
            except NameError:
                pass
            except TypeError:
                pass
            except IndexError:
                pass

            
            if debug and ctype != '':
                print('BtComm: (#',len(cmd_array),') got command',ctype,cdata)
         
            if ctype == 'scan':
                self.doScan(cdata)
            elif ctype == 'ping':
                self.doPing(cdata)
            elif ctype.startswith('pushjar'):
                self.doPush(cdata,ctype[7:],False)
            elif ctype.startswith('pushjad'):
                self.doPush(cdata,ctype[7:],True)
            elif ctype == 'info':
                self.doInfo(cdata)
            elif ctype == 'test_at':
                self.doAT(cdata)
            else:
                time.sleep(1)
                
    def setStatus(self, address, status):
        global debug, bt_devices, browserWin

        for k, v in bt_devices.items():
 
            if address == v[0]:
                if status != v[5]:
                    v[5] = status
                
                needRun = v[4]  
                if status == AR_AVAIL and needRun != '':
                    if debug: print('run anyRemote with',needRun)
                        
                    startAnyRemote(needRun)
                    
                try:
                    if browserWin != None:
                        sendEvent(browserWin,30000," ") 
                except (AttributeError, NameError):
                    pass

                return

    def doScan(self,force):
        global debug
        if debug: print('BtComm: scan devices')
        existing = getDevSet()
        
        # Search new devices
        nearby_devices = []
        try:
            nearby_devices = bluetooth.discover_devices(lookup_names = True)
        except bluetooth.BluetoothError as msg:
            if debug: print("BtComm: BluetoothError")
            
            # sleep additional 5 sec
            t2 = 0
            while self.keepRun and t2 < 5:
                time.sleep(0.1)
                t2 = t2 + 0.1
        except OSError as msg:
            if debug: print('BtComm: OSError',msg)
            return
       
        if debug: print("BtComm: found %d devices" % len(nearby_devices))

        availSet = getAvailableSet()
        
        for addr,name in nearby_devices:
            if debug: print("  %s - %s" % (addr, name))
        
            channels = ''
            
            services = bluetooth.find_service(address=addr)
            
            for svc in services:
                try:
                    if debug: print(addr,svc["name"],svc["port"])
                
                    if svc["name"] == 'Serial Port' or svc["name"].startswith('COM ') or svc["name"].startswith('Dial-up') or svc["name"].startswith('Dialup'):
                        
                        if channels != '':
                            channels = channels+':'
                        channels = channels+str(svc["port"])
                    
                except (AttributeError, NameError):
                    pass
            if addr not in existing:
                showDetailsWin([addr, name, name, channels, '', True])
            else:
                if addr in availSet:
                    availSet.remove(addr)
                else:
                    self.setStatus(addr,AR_AVAIL)
            
        for rest in availSet:
            self.setStatus(rest, '')
        
        if debug: print('BtComm: verify done')
        
        if force == 'T':
            stopPBar()

    def doPing(self, data):
        global debug, detailsWin
        if debug: print('BtComm: ping device')
        
        sendEvent(detailsWin,31100,'') 
        writePBar(AR_CONN_DEV)
        
        os.system('killall -9 hcitool 2> /dev/null')
        if debug: print('hcitool name '+data)
        ret = getResult('hcitool name '+data,'browser')
        
        stopPBar()
        if (ret == ''):
            sendEvent(detailsWin,31100,_tr('Ping failed !')) 
        else:
            sendEvent(detailsWin,31100,_tr('Ping OK !')) 

    def doPush(self, data, size, usejad):
        global debug
        if debug: print('BtComm: obex push')

        sendEvent(detailsWin,31100,'') 
        stopPBar()   
        
        dash = '-'
        if size == '':
            dash = ''
        midlet = 'anyRemote' + dash + size + '.jar'
	
        if not os.path.exists(self.midletDir):       
            sendEvent(detailsWin,31100,_tr('Can not find J2ME midlet ('+midlet+_tr(') to upload !')))
            return
        
        path = self.midletDir + os.sep + midlet
        if not os.path.isfile(path):
            sendEvent(detailsWin,31100,_tr('Can not find J2ME midlet ('+midlet+_tr(') to upload !')))
            return
                     
        sender = ''    
        suffix = ' '    
        if (isInstalled('gnome-obex-send') == 'OK'):
            sender = 'gnome-obex-send -d '
        elif (isInstalled('bluetooth-sendto') == 'OK'):
            sender = 'bluetooth-sendto --dest='
        elif (isInstalled('kbtobexclient') == 'OK'):
            sender = 'kbtobexclient'
        elif (isInstalled('bluedevil-sendfile') == 'OK'):
            sender = 'bluedevil-sendfile -u '
            suffix = ' -f '
        elif (isInstalled('blueman-sendto') == 'OK'):
            sender = 'blueman-sendto -d '
        else:
            sendEvent(detailsWin,31100,_tr('None of bluedevil-sendfile, gnome-obex-send, bluetooth-sendto, blueman-sendto and kbtobexclient are installed !'))
            return 
               
        if debug: print(sender + data + suffix + path)

        ret = getResult(sender + data + suffix + path + ' &','browser')
        
        if usejad:
            jad = 'anyRemote' + dash + size + '.jad'
            path = self.midletDir + os.sep + jad
            if not os.path.isfile(path):
                sendEvent(detailsWin,31100,_tr('Can not find JAD file ('+jad+_tr(') to upload !')))
                return
            
            ret = getResult(sender + data + suffix + path + ' &','browser')
        
    def doInfo(self,data):
        global debug
        if debug: print('BtComm: get info')

        writePBar('Connecting to device')
        
        services = bluetooth.find_service(address=data)

        if len(services) > 0:
            print("found %d services on %s" % (len(services), data))
            print("")
        else:
            print("no services found")

        for svc in services:
            print("Service Name: %s"    % svc["name"])
            print("    Host:        %s" % svc["host"])
            print("    Description: %s" % svc["description"])
            print("    Provided By: %s" % svc["provider"])
            print("    Protocol:    %s" % svc["protocol"])
            print("    channel/PSM: %s" % svc["port"])
            print("    svc classes: %s "% svc["service-classes"])
            print("    profiles:    %s "% svc["profiles"])
            print("    service id:  %s "% svc["service-id"])
            print("")

    def getSerialPort(self,addr):
        services = bluetooth.find_service(address=addr)
        for svc in services:
            try:
                if svc["name"] == 'Serial Port' or svc["name"].startswith('COM ') or svc["name"].startswith('Dial-up') or svc["name"].startswith('Dialup'):
                    return svc["port"]
            except (AttributeError, NameError):
                pass
        return None
        
    def send_at(self,sock,cmd):
        global debug
        if len(cmd) == 0: 
            return ''
        
        data = cmd+'\r'
        if debug: print("send",cmd)
        
        sock.send(cmd+'\r')
        time.sleep(0.5)
        data = sock.recv(1024)

        if debug: print("got",data)
        return data

    def doAT(self,data):
        global debug
        if debug: print('BtComm: doAT')
            
        sendEvent(detailsWin,31100,'')
        writePBar(AR_CONN_DEV)        
        port = self.getSerialPort(data)
        if debug: print('BtComm: port',port)
        
        if port == None:
            sendEvent(detailsWin,31100,_tr('Can not get port to connect to. Is there any device available ?'))
            pass
        else:
            sock=bluetooth.BluetoothSocket( bluetooth.RFCOMM )
            try:
                sock.connect((data, port))
            except bluetooth.BluetoothError as msg:
                d = str(msg)
                stopPBar()
                if d.startswith('(111,'):
                    sendEvent(detailsWin,31100,_tr('Connection refused'))
                return

            ret = self.send_at(sock,"ATZ")
            ret = self.send_at(sock,"ATE0")
            ret = self.send_at(sock,"AT+CMER=?")
            
            status = '' 
            if ret.find('ERROR') == -1:
                ret = ret[ret.index('+CMER'):]
                ret = (ret.split('\n'))[0]
            
                AT_CMER=ret.replace(' ','').replace('\r','')
            
                #+CMER: (0,3),(0),(0),(0,1,2),(0)        <- bad
                #+CMER: (0,3),(0,1,2),(0),(0,1,2),(0)        <- OK
                s1 = (AT_CMER.split('('))[2]
                s2 = (s1.split(')'))[0]
                
                status = AT_CMER
                if s2 == '0':
                    status = _tr('AT mode is not supported by phone (')+status+')'
                else:
                    status = _tr('AT mode could be supported by phone (')+status+')'
            else:
                status = _tr('AT mode is not supported by phone (ERROR response)')
                
            sendEvent(detailsWin,31100,status)
            sock.close()

        stopPBar()
   
    def stop(self):
        global debug
        if debug: print('BtComm stop')
        self.keepRun = False

##################################################################################
#
# Device Browser
#
##################################################################################
class DeviceBrowser(QMainWindow):

    def __init__(self,parent = None,fl = 0):
        global mainWIndow
    
        QMainWindow.__init__(self)
 
        self.setCentralWidget(QWidget(self))
        deviceBrowserLayout = QGridLayout(self.centralWidget())
        deviceBrowserLayout.setContentsMargins(6,6,6,6)
        deviceBrowserLayout.setSpacing(6)

        self.treeview = QTreeWidget(self.centralWidget())
        self.treeview.setColumnCount(4)
        self.treeview.headerItem().setText(0,_tr("Name")+"    ")
        self.treeview.headerItem().setText(0,_tr("Device Name")+"    ")
        self.treeview.headerItem().setText(0,_tr("Address")+"    ")
        self.treeview.headerItem().setText(0,_tr("Status")+"    ")
        
        self.treeview.header().setSectionResizeMode(0,QHeaderView.ResizeToContents)
        self.treeview.header().setSectionResizeMode(1,QHeaderView.ResizeToContents)
        self.treeview.header().setSectionResizeMode(2,QHeaderView.ResizeToContents)
        self.treeview.header().setSectionResizeMode(3,QHeaderView.Stretch)

        deviceBrowserLayout.addWidget(self.treeview,0,0)

        self.scanAction    = QAction(loadIcon("zoom-in",     "filefind"),  _tr("Scan for devices"), self)
        self.detailsAction = QAction(loadIcon("configure",   "configure"), _tr("Details"), self)
        self.deleteAction  = QAction(loadIcon("edit-delete", "editdelete"),_tr('Delete'), self)
        self.closeAction   = QAction(loadIcon("dialog-close","fileclose"), _tr('Close'), self)
        
        self.MenuBar = self.menuBar()

        self.fileMenu = self.MenuBar.addMenu(_tr("File"))
        self.fileMenu.addAction(self.scanAction)
        self.fileMenu.addAction(self.detailsAction)
        self.fileMenu.addAction(self.deleteAction)
        self.fileMenu.addAction(self.closeAction)

        self.languageChange()

        self.resize(QSize(500,250).expandedTo(self.minimumSizeHint()))

        self.scanAction.triggered.connect(self.scan)
        self.detailsAction.triggered.connect(self.details)
        self.deleteAction.triggered.connect(self.deleteDev)
        self.closeAction.triggered.connect(self.closeAct)

        self.treeview.itemDoubleClicked.connect(self.listDClicked)
        
        self.treeview.setSelectionMode(QAbstractItemView.SingleSelection)
        self.treeview.sortByColumn(0,Qt.AscendingOrder)
        
        self.populateDeviceList()

    def languageChange(self):
        self.setWindowTitle(_tr("Device Browser"))
        self.treeview.headerItem().setText(0,_tr("Name"))
        self.treeview.headerItem().setText(1,_tr("Device Name"))
        self.treeview.headerItem().setText(2,_tr("Address"))
        self.treeview.headerItem().setText(3,_tr("Status"))
        self.treeview.clear()

        self.scanAction.setText(_tr("Scan for devices"))
        self.detailsAction.setText(_tr("Details"))
        self.deleteAction.setText(_tr('Delete'))
        self.closeAction.setText(_tr('Close'))
        self.fileMenu.menuAction().setText(_tr("File"))
        
        self.detailsWin = None
        self.selected = None

    def populateDeviceList(self):
        global bt_devices
        self.treeview.clear()
        
        for k, v in bt_devices.items():
            lvi = QTreeWidgetItem(self.treeview)
            lvi.setText(0,bt_devices[k][2])       
            lvi.setText(1,bt_devices[k][1])
            lvi.setText(2,bt_devices[k][0])
            lvi.setText(3,bt_devices[k][5])

    def scan(self):
        stopPBar () 
        startPBar(_tr('Scanning'))
        browseDevices('T')

    def details(self):
        global bt_devices
        
        idx = 0
        item = None
        while idx < self.treeview.topLevelItemCount():
            if self.treeview.isItemSelected(self.treeview.topLevelItem(idx)):
                item = self.treeview.topLevelItem(idx)
                break
            idx = idx + 1 

        if item == None:
            return
        try:
            row = bt_devices[str(item.text(2))]
        except KeyError:
            print('DeviceBrowser.details: KeyError')
            return
            
        showDetailsWin([row[0],row[1],row[2],row[3],row[4],False])

    def listDClicked(self, listItem, column):
        self.details()

    def deleteDev(self):
        global bt_devices
         
        idx = 0
        while idx < self.treeview.topLevelItemCount():
            if self.treeview.isItemSelected(self.treeview.topLevelItem(idx)):
                btAddr = str(self.treeview.takeTopLevelItem(idx).text(2))
                
                if btAddr in bt_devices:
                    del bt_devices[btAddr]
                
                break
            idx = idx + 1 

    def closeEvent(self, event):
        self.closeAct()
        
    def closeAct(self):
        self.hide()
        saveDevices()

    def customEvent(self,event):
        if event.type() == 30000:
            self.populateDeviceList()

##################################################################################
#
# Device Details
#
##################################################################################
class DeviceDetail(QMainWindow):
    def __init__(self, ba, m, n, channels, cfgFile, is_new, jDir):
        global mainWindow

        QMainWindow.__init__(self)
        self.statusBar()

        self.model     = str(m).replace('\n','')
        self.dname     = str(n).replace('\n','')
        self.btAddr    = str(ba)
        self.channels  = channels
        self.cfile     = str(cfgFile)
        self.isNew     = str(is_new)
        self.midletDir = jDir

        self.setCentralWidget(QWidget(self))
        deviceDetailLayout = QGridLayout(self.centralWidget())
        deviceDetailLayout.setContentsMargins(6,6,6,6)
        deviceDetailLayout.setSpacing(6)

        self.deviceName = QLabel(self.centralWidget())
        deviceDetailLayout.addWidget(self.deviceName,0,0)
        
        self.deviceNameValue = QLabel(self.centralWidget())
        deviceDetailLayout.addWidget(self.deviceNameValue,0,1)

        self.btAddress = QLabel(self.centralWidget())
        deviceDetailLayout.addWidget(self.btAddress,0,3,1,2)
        
        self.btAddressValue = QLabel(self.centralWidget())
        deviceDetailLayout.addWidget(self.btAddressValue,0,5,1,2)

        self.specifyName = QLabel(self.centralWidget())
        deviceDetailLayout.addWidget(self.specifyName,1,0)
        
        self.devName = QLineEdit(self.centralWidget())
        deviceDetailLayout.addWidget(self.devName,1,1,1,6)

        self.runWhen = QCheckBox(self.centralWidget())
        self.runWhen.setToolTip(_tr('anyRemote will start only if no other instances of anyRemote are running'))
        deviceDetailLayout.addWidget(self.runWhen,2,0,1,3)
        
        self.fileWhen = QLineEdit(self.centralWidget())
        deviceDetailLayout.addWidget(self.fileWhen,3,0,1,6)
        self.choose = QPushButton(self.centralWidget())
        deviceDetailLayout.addWidget(self.choose,3,6)

        self.kSeparator1 = QFrame(self.centralWidget())
        self.kSeparator1.setFrameShape(QFrame.HLine)
        self.kSeparator1.setFrameShadow(QFrame.Sunken)
        self.kSeparator1.setFrameShape(QFrame.HLine)
        deviceDetailLayout.addWidget(self.kSeparator1,4,0,1,6)

        self.uploadj2me = QPushButton(self.centralWidget())
        deviceDetailLayout.addWidget(self.uploadj2me,5,0)
        
        self.iSet = QLabel(self.centralWidget())
        deviceDetailLayout.addWidget(self.iSet,5,1)
        
        self.buttons = QButtonGroup(self.centralWidget())
        
        self.sz16 = QRadioButton(self.centralWidget())
        deviceDetailLayout.addWidget(self.sz16,5,2)
        self.sz16.setChecked(True)

        self.sz32 = QRadioButton(self.centralWidget())
        deviceDetailLayout.addWidget(self.sz32,5,3)

        self.sz48 = QRadioButton(self.centralWidget())
        deviceDetailLayout.addWidget(self.sz48,5,4)

        self.sz64 = QRadioButton(self.centralWidget())
        deviceDetailLayout.addWidget(self.sz64,5,5)
 
        self.sz128 = QRadioButton(self.centralWidget())
        deviceDetailLayout.addWidget(self.sz128,5,6)

        self.szAll = QRadioButton(self.centralWidget())
        deviceDetailLayout.addWidget(self.szAll,6,2,1,2)
        
        self.buttons.addButton(self.sz16)
        self.buttons.addButton(self.sz32)
        self.buttons.addButton(self.sz48)
        self.buttons.addButton(self.sz64)
        self.buttons.addButton(self.sz128)
        self.buttons.addButton(self.szAll)
        
        self.sz32.setChecked(True)

        self.uplJad = QCheckBox(self.centralWidget())
        self.uplJad.setToolTip(_tr('Can be useful for Samsung phones'))
        deviceDetailLayout.addWidget(self.uplJad,7,0,1,2)

        self.bigTitleIcon = QCheckBox(self.centralWidget())
        self.bigTitleIcon.setToolTip(_tr('16x16 and 64x64 title icons are available'))
        deviceDetailLayout.addWidget(self.bigTitleIcon,7,2,1,4)
        
        self.noJsr82 = QCheckBox(self.centralWidget())
        self.noJsr82.setToolTip(_tr('Can be used on WinMobile devices'))
        deviceDetailLayout.addWidget(self.noJsr82,8,0,1,4)

        self.ping = QPushButton(self.centralWidget())
        deviceDetailLayout.addWidget(self.ping,9,0)
        self.testat = QPushButton(self.centralWidget())
        deviceDetailLayout.addWidget(self.testat,9,1,1,2)

        self.close = QPushButton(self.centralWidget())
        deviceDetailLayout.addWidget(self.close,9,4,1,2)
        self.ok = QPushButton(self.centralWidget())
        deviceDetailLayout.addWidget(self.ok,9,6)

        self.languageChange()
        
        self.choose.clicked.connect(self.chooseAction)
        self.ping.clicked.connect(self.pingAction)
        self.testat.clicked.connect(self.testatAction)
        self.uploadj2me.clicked.connect(self.uploadjAction)
        self.close.clicked.connect(self.closeAction)
        self.ok.clicked.connect(self.okAction)
        
        self.runWhen.toggled.connect(self.runWhenChecked)
        
        self.szAll.toggled.connect(self.clicked_szAll)
        self.bigTitleIcon.toggled.connect(self.clicked_bti)
        self.noJsr82.toggled.connect(self.clicked_nojsr82)

        self.deviceNameValue.setText(self.model)
        self.btAddressValue.setText(self.btAddr)
        self.devName.setText(self.dname);
        
        if self.cfile == '':
            self.fileWhen.setEnabled(False)
            self.choose.setEnabled(False)
        else:
            self.fileWhen.setEnabled(True)
            self.fileWhen.setText(self.cfile)
            self.runWhen.setChecked(True)
            self.choose.setEnabled(True)
           
        if not os.path.isdir(self.midletDir): 
            self.uploadj2me.setEnabled(False)
            self.uploadj2me.setToolTip(_tr('It needs to install anyremote-j2me-client package first'))

        bt,msg = btVerify()
        if bt == 'NOK': 
            print('DeviceDetail: ',msg)
            
            self.ping.setEnabled(False)
            self.ping.setToolTip(_tr('Bluetooth service is not active'))
            self.testat.setEnabled(False)
            self.testat.setToolTip(_tr('Bluetooth service is not active'))
            self.uploadj2me.setEnabled(False)
            self.bigTitleIcon.setEnabled(False)

    def runWhenChecked(self,w):
        if self.runWhen.isChecked():
            self.fileWhen.setEnabled(True)
            self.choose.setEnabled(True)
        else:
            self.fileWhen.setEnabled(False)
            self.fileWhen.setText('')
            self.choose.setEnabled(False)

    def clicked_szAll(self,w):
        if self.szAll.isChecked():
            self.bigTitleIcon.setChecked(False)

    def clicked_bti(self,w):
        if self.szAll.isChecked() and not self.noJsr82.isChecked():
            self.bigTitleIcon.setChecked(False)
                      
        if not self.bigTitleIcon.isChecked():
            self.noJsr82.setChecked(False)

    def clicked_nojsr82(self,w):
        
        self.szAll.setEnabled(not self.noJsr82.isChecked())
        self.sz16.setEnabled (not self.noJsr82.isChecked())
        self.sz32.setEnabled (not self.noJsr82.isChecked())
        self.sz48.setEnabled (not self.noJsr82.isChecked())
        self.sz64.setEnabled (not self.noJsr82.isChecked())

        if self.noJsr82.isChecked():
            self.bigTitleIcon.setChecked(True)
    
    def chooseAction(self):
        global chooserWin
        self.fileWhen.setEnabled(False)
        self.choose.setEnabled(False)
        chooserWin = Chooser(self)
        chooserWin.show()

    def closeEvent(self,arg):
        self.closeAction()

    def closeAction(self):
        self.hide()
    
    def sendReRead(self):
        global browserWin
        sendEvent(browserWin,30000," ") 
    
    def okAction(self):
        global bt_devices

        for k, v in bt_devices.items():
            
            row = bt_devices[k]
            addr = row[0]
            
            if self.btAddr == addr:

                if self.devName.text() != row[2]:
                    row[2] = self.devName.text()
                    
                if self.model != row[1]:
                    row[1] = self.model

                cf = self.fileWhen.text()
                if cf != row[4]:
                    row[4] = cf
                
                self.sendReRead()
                self.hide()
                return

        bt_devices[self.btAddr] = [self.btAddr,self.model,str(self.devName.text()),self.channels,str(self.fileWhen.text()),AR_AVAIL]

        self.sendReRead()
        self.hide()
    
    def testatAction(self):
        global debug
        if debug: print('Queue test AT')
            
        stopPBar () 
        startPBar(AR_WAIT_OPS)
        queueBT('test_at',self.btAddr)
    
    def uploadjAction(self):
        global debug
        if debug: print('Queue push request')
        
        sz = '16'
        if self.noJsr82.isChecked():
            sz = 'nojsr82'
        elif self.szAll.isChecked():
            sz = ''
        else:
            if self.sz32.isChecked():
                sz = '32'
            elif self.sz48.isChecked():
                sz = '48'
            elif self.sz64.isChecked():
                sz = '64'
            elif self.sz128.isChecked():
                sz = '128'
               
            if self.bigTitleIcon.isChecked():
                sz = sz+'b'
           
        push = 'pushjar'
        if self.uplJad.isChecked():
            push = 'pushjad'
           
        stopPBar () 
        startPBar(AR_WAIT_OPS)
        queueBT(push+sz,self.btAddr)
    
    def pingAction(self):
        global debug
        if debug: print('Queue ping request')

        stopPBar () 
        startPBar(_tr('Wait ping results'))
        queueBT('ping',self.btAddr)
        
    def setStatustext(self,text):
        self.statusBar().clearMessage()
        self.statusBar().showMessage(text)

    def customEvent(self,event):
        if event.type() == 31000:
            self.cfile = event.getData()
            self.fileWhen.setText(self.cfile)
            self.fileWhen.setEnabled(True)
            self.choose.setEnabled(True)
        elif event.type() == 32000:
            self.fileWhen.setEnabled(True)
            self.choose.setEnabled(True)
        elif event.type() == 31100:
            self.setStatustext(event.getData())

    def languageChange(self):
        self.setWindowTitle(_tr("Device Parameters"))
        self.deviceNameValue.setText("")
        self.btAddress.setText(_tr("BT address")+': ')
        self.btAddressValue.setText("")
        self.deviceName.setText(_tr("Device Name")+':')
        self.specifyName.setText(_tr("Specify Name")+': ')
        self.iSet.setText(_tr(" with icon set "))
        self.runWhen.setText(_tr("Run anyRemote when discovered"))
        self.bigTitleIcon.setText(_tr("use big title icon"))
        self.noJsr82.setText(_tr("use version without JSR-82"))
        self.uplJad.setText(_tr("Also upload JAD"))
        self.sz16.setText("16x16")
        self.sz32.setText("32x32")
        self.sz48.setText("48x48")
        self.sz64.setText("64x64")
        self.sz128.setText("128x128")
        self.szAll.setText(_tr("with all icon sets"))
        self.choose.setText(_tr('Choose'))
        self.ping.setText(_tr("Ping"))
        self.uploadj2me.setText(_tr("Upload J2ME"))
        self.testat.setText(_tr("Test AT"))
        self.close.setText(_tr('Close'))
        self.ok.setText(_tr('OK'))

##################################################################################
#
# Configuration checker
#
##################################################################################
class CfgChecker(QMainWindow):

    def __init__(self,parent = None,fl = 0):
    
        global cfgReader
        
        QMainWindow.__init__(self)

        self.setCentralWidget(QWidget(self))
        cfgCheckerLayout = QGridLayout(self.centralWidget())
        cfgCheckerLayout.setContentsMargins(6,6,6,6)
        cfgCheckerLayout.setSpacing(6)

        self.treeview = QTreeWidget(self.centralWidget())
        self.treeview.setColumnCount(2)
        self.treeview.headerItem().setText(0,_tr("Package")+"    ")
        self.treeview.headerItem().setText(1,_tr("Status")+"         ")
        
        self.treeview.header().setSectionResizeMode(0,QHeaderView.ResizeToContents)
        self.treeview.header().setSectionResizeMode(1,QHeaderView.Stretch)

        cfgCheckerLayout.addWidget(self.treeview,0,0)

        self.closeAction   = QAction(loadIcon("dialog-close","fileclose"), _tr("Close"), self)

        self.MenuBar = self.menuBar()

        self.fileMenu = self.MenuBar.addMenu(_tr("File"))
        self.fileMenu.addAction(self.closeAction)
 
        self.languageChange()

        self.resize(QSize(450,250).expandedTo(self.minimumSizeHint()))

        self.closeAction.triggered.connect(self.closeAct)
        
        self.treeview.setSelectionMode(QAbstractItemView.SingleSelection)
        self.treeview.setSortingEnabled(True)
        self.treeview.sortByColumn(0,Qt.DescendingOrder)
        self.treeview.setRootIsDecorated(False)
        
        pbs = AR_NOTINSTALLED
        if pybluez: pbs = AR_INSTALLED
        
        ars = isInstalled('anyremote')
        if ars == 'NOK':
            ars = AR_NOTINSTALLED
        else:
            ars = AR_INSTALLED

        bus = isInstalled('sdptool')
        if bus == 'NOK':
            bus = AR_NOTINSTALLED
        else:
            bus = AR_INSTALLED

        jDir = getJ2MEPath()
        jVer = ''
        ajs = AR_INSTALLED
        if jDir == '':
            ajs = AR_NOTINSTALLED
        else:
            jVer = getJ2MEClientVersion(jDir+os.sep+'anyRemote-16.jad')
            
        if jDir == os.environ.get("HOME")+os.sep+'.anyRemote':
            ajs = _tr('Downloaded')
        
        ajs = ajs+' v.'+jVer

        state,bts = btVerify()
    
        lvi = QTreeWidgetItem(self.treeview)
        lvi.setText(0,"anyRemote")       
        lvi.setText(1,ars)
        
        lvi = QTreeWidgetItem(self.treeview)
        lvi.setText(0,"Bluez utilities")       
        lvi.setText(1,bus)

        lvi = QTreeWidgetItem(self.treeview)
        lvi.setText(0,"PyBluez")       
        lvi.setText(1,pbs)

        lvi = QTreeWidgetItem(self.treeview)
        lvi.setText(0,"anyremote-j2me-client")       
        lvi.setText(1,ajs)

        if jDir != cfgReader.j2meDir_:
            lvi.setExpanded(True)
            lvi2 = QTreeWidgetItem(lvi)
            lvi2.setText(0,_tr('Warning:'))       
            lvi2.setText(1,_tr('Installation directory of anyremote-j2me-client not specified in current setup configuration !'))

        lvi = QTreeWidgetItem(self.treeview)
        lvi.setText(0,"Bluetooth service")       
        lvi.setText(1,bts)
 
    def languageChange(self):
        self.setWindowTitle(_tr("Configuration Check"))
        self.treeview.headerItem().setText(0,_tr("Package")+"    ")
        self.treeview.headerItem().setText(1,_tr("Status")+"         ")
        self.treeview.clear()
        item = QTreeWidgetItem(self.treeview,None)

        self.closeAction.setText(_tr('Close'))
        self.fileMenu.menuAction().setText(_tr("File"))
        
        self.cfgChkWin = None
        self.selected = None

    def closeEvent(self, event):
        self.closeAct()

    def closeAct(self):
        self.hide()

##################################################################################
#
# Choose app. to manage when phone is discovered
#
##################################################################################

class Chooser(QMainWindow):

    def __init__(self,parent = None,fl = 0):
        QMainWindow.__init__(self)
        
        self.parent = parent
        
        self.setCentralWidget(QWidget(self))
        chooserLayout = QGridLayout(self.centralWidget())
        chooserLayout.setContentsMargins(6,6,6,6)
        chooserLayout.setSpacing(6)

        self.listView1 = QTreeWidget(self.centralWidget())
        
        self.listView1.setColumnCount(3)
        self.listView1.headerItem().setText(0,_tr("Application")+"    ")
        self.listView1.headerItem().setText(1,_tr("Mode")+"    ")
        self.listView1.headerItem().setText(2,_tr("F")+"    ")

        self.listView1.header().setSectionResizeMode(0,QHeaderView.ResizeToContents)
        self.listView1.header().setSectionResizeMode(1,QHeaderView.Stretch)
        
        self.listView1.setSelectionMode(QAbstractItemView.SingleSelection)
        self.listView1.hideColumn(2)
        self.listView1.sortByColumn(0,Qt.AscendingOrder)
        
        self.listView1.itemDoubleClicked.connect(self.listDClicked)
        
        chooserLayout.addWidget(self.listView1,0,0,1,2)

        self.ok = QPushButton(self.centralWidget())
        chooserLayout.addWidget(self.ok,1,0)
        
        self.cancel = QPushButton(self.centralWidget())
        chooserLayout.addWidget(self.cancel,1,1)


        self.languageChange()

        self.resize(QSize(350,400).expandedTo(self.minimumSizeHint()))

        self.cancel.clicked.connect(self.cancelAction)
        self.ok.clicked.connect(self.okAction)
        
        self.populateChooseList()
    
    def populateChooseList(self):
        global appData
        
        for k, v in appData.items():
            lvi = QTreeWidgetItem(self.listView1)
            
            ic = loadAppIcon(v[1])
            lvi.setIcon(0,ic)
            lvi.setText(0,v[0])          
            lvi.setText(1,v[7])
            lvi.setText(2,str(k))
                    
    def listDClicked(self, listItem, column):
        self.okAction()

    def cancelAction(self):
        sendEvent(self.parent,32000,'') 
        self.hide()

    def okAction(self):
        global appData

        idx  = 0
        item = None
        while idx < self.listView1.topLevelItemCount():
            item = self.listView1.topLevelItem(idx)
            if self.listView1.isItemSelected(self.listView1.topLevelItem(idx)):
                item = self.listView1.topLevelItem(idx)
                break
            idx = idx + 1

        if item == None:
            return

        try:
            idx = int(str(item.text(2)))
        except ValueError:
            return
        try:
            row = appData[idx]
        except KeyError:
            return
        
        sendEvent(self.parent,31000,appData[idx][5]) 
        self.hide()
   
    def closeEvent(self, event):
        self.cancelAction()

    def languageChange(self):
        self.setWindowTitle(_tr("Choose application"))
        self.listView1.headerItem().setText(0,_tr("Application"))
        self.listView1.headerItem().setText(1,_tr("Mode"))
        self.listView1.clear()
        item = QTreeWidgetItem(self.listView1,None)

        self.cancel.setText(_tr('Close'))
        self.ok.setText(_tr('OK'))


##################################################################################
#
# Progress bar window
#
##################################################################################
class PBar(QProgressDialog):
    def __init__(self, text, steps):
        QProgressDialog.__init__(self, text, '', 0,steps)
        
        bar = QProgressBar(self)
        bar.setTextVisible(False)
        self.setBar(bar)
        self.setMinimum(0)
        self.setMaximum(10)
        self.setCancelButton(None)
        self.setAutoReset(True)
        self.setAutoClose(False)
        self.show()

def sendMainWindow(code,text):
    global mainWindow
    if mainWindow:
        sendEvent(mainWindow, code, text)        

def startPBar(text):
    sendMainWindow(20019, text)        

def stopPBar():
    sendMainWindow(20018, '')        
        
def writePBar(text):
    sendMainWindow(20021, text)        
        
def timerPBar():
    global pbar, pbarTimer, quitFlag

    if not quitFlag:
            try:
                pbar.setValue(pbar.value()+1)
            except (AttributeError, NameError):
                return
            pbarTimer = QTimer.singleShot(200,timerPBar)
      
##################################################################################
#
# Frontend to anyRemote
#
##################################################################################

class FrontEnd(threading.Thread):
    def __init__(self):
        global debug
        threading.Thread.__init__(self)        
        self.isReady = False
        if debug: print('FrontEnd init')

    def ready(self):
        return self.isReady

    def stop(self):
        global debug
        if debug: print('FrontEnd stop')
        self.isRun = False

    def run(self):
        global port, cmdToSend, debug
        if debug: print('FrontEnd thread is running', port)

        self.isRun = True
        s = None
        for res in socket.getaddrinfo('localhost', port, socket.AF_INET, socket.SOCK_STREAM, 0, socket.AI_PASSIVE):
            af, socktype, proto, canonname, sa = res

            try:
                s = socket.socket(af, socktype, proto)
                s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
                s.setblocking(0)
            except socket.error as msg:
                s = None
                continue
            if debug: print('FrontEnd socket created')
                    
            try:
                s.bind(sa)
                s.listen(1)
            except socket.error as msg:
                s.close()
                s = None
                continue
            if debug: print('FrontEnd socket after listen')
            break
                
        if s is None:
            if debug: print('FrontEnd could not open socket')
            sys.exit(1)

        while self.isRun:
            while self.isRun:

                self.isReady = True
                try:
                    conn, addr = s.accept()
                    if debug: print('accept', addr)
                    break
                except socket.error as msg:
                    time.sleep(0.1)

            if self.isRun:
                self.processOneConnection(conn, addr)

        if debug: print('Close server socket' )
        s.close()

    def processOneConnection(self,conn,addr):
        global cmdToSend, debug

        if debug: print('FrontEnd processOneConnection', addr)

        self.isConn = True
        while self.isRun and self.isConn:

            conn.setblocking(0)

            if debug: print('FrontEnd.Connected by', addr)

            sendMainWindow(20003,'')

            while self.isRun and self.isConn:

                time.sleep(0.2)

                data = ''
                try:
                    data = conn.recv(512)
                    if debug: print('Got from backend ', data)
                        
                    if self.isRun:
                        sendMainWindow(10000,data)

                    if not data: 
                        if debug: print('FrontEnd anyRemote die?')

                        if self.isRun:
                            sendMainWindow(20000,'') 
                            self.isConn = False
                        break

                except socket.error as msg:
                    pass

                try:
                    if cmdToSend:
                        if debug: print('Send to backend '+cmdToSend + "\n")
                        conn.sendall(cmdToSend)
                        cmdToSend = '';
                except (socket.error, NameError):
                    if debug: print('FrontEnd.processOneConnection exception')
                    pass

            if debug: print('Close connection')
            sendMainWindow(20012,'')  
            conn.close()

##################################################################################
#
# Edit window
#
##################################################################################

class EditWin(QMainWindow):
    def __init__(self,efile,parent = None,fl = 0):
        QMainWindow.__init__(self)
        
        self.eFile    = efile

        self.setCentralWidget(QWidget(self))
        editWinLayout = QGridLayout(self.centralWidget())
        editWinLayout.setContentsMargins(6,6,6,6)
        editWinLayout.setSpacing(6)
 
        self.textEdit1 = QTextEdit(self.centralWidget())

        editWinLayout.addWidget(self.textEdit1,0,0)

        self.saveEditor   = QAction(loadIcon("document-save",   "filesave"),  _tr("Save"),   self)
        self.saveAsEditor = QAction(loadIcon("document-save-as","filesaveas"),_tr("Save As"),self)
        self.closeEditor  = QAction(loadIcon("dialog-close",    "fileclose"), _tr("Close"),  self)
        
        self.Menu = self.menuBar()
        
        self.fileMenu = self.Menu.addMenu(_tr("File"))
        self.fileMenu.addAction(self.saveEditor)
        self.fileMenu.addAction(self.saveAsEditor)
        self.fileMenu.addAction(self.closeEditor)

        self.languageChange()

        self.resize(QSize(550,600).expandedTo(self.minimumSizeHint()))
        #self.clearWState(Qt.WState_Polished)
        
        self.saveEditor.triggered.connect(self.saveFile)
        self.saveAsEditor.triggered.connect(self.saveAs)
        self.closeEditor.triggered.connect(self.cancel_clicked)
        
        self.loadEditFile()
        
    def loadEditFile(self):
    
        infile = None
        
        writable = False
        try:
            infile = open(self.eFile, "r")
            writable = QFileInfo(self.eFile).isWritable()
        except IOError:
            pass
            
        if infile:
            string = infile.read()
            infile.close()
            self.textEdit1.setText(string)
            self.textEdit1.setReadOnly(not writable)
        else:
            self.eFile = ""

    def languageChange(self):
        self.setWindowTitle(_tr("Edit configuration file"))
        self.closeEditor.setText(_tr("Close"))
        self.saveEditor.setText(_tr("Save"))
        self.saveEditor.setShortcut(QKeySequence(_tr("Ctrl+S")))
        self.saveAsEditor.setText(_tr("Save As"))
        self.fileMenu.menuAction().setText(_tr("File"))


    def saveFile(self):
        try:
            f=open(self.eFile, 'w')
            if f:
                #text = self.textEdit1.text() not work ?
                text = self.textEdit1.document().toPlainText()
                f.write(str(text))
                f.close()
                self.deleteLater()   

        except IOError:
            sendMainWindow(20016, _tr("Can not save the file !"))
            self.saveAs()
 
    def saveAs(self):
        f = QFileDialog.getSaveFileName(None,"", "");
        if f:
            self.eFile = f
            self.saveFile()
        
    def cancel_clicked(self):
        self.deleteLater()   

    def closeEvent(self, event):
        self.cancel_clicked()
        
##################################################################################
#
# Preferences window
#
##################################################################################

class Preferences(QMainWindow):
    def __init__(self):
        
        global cfgReader
        
        QMainWindow.__init__(self)
 
        self.setMinimumSize(QSize(670,300))

        self.setCentralWidget(QWidget(self))
        prefsLayout = QGridLayout(self.centralWidget())
        prefsLayout.setContentsMargins(6,6,6,6)
        prefsLayout.setSpacing(6)
        
        self.tabs = QTabWidget(self.centralWidget())
        prefsLayout.addWidget(self.tabs,0,0,5,5)

        #############################
        ### Common tab
        #############################
        commonFrame = QFrame()
        self.tabs.addTab(commonFrame,_tr("General"))
        PreferencesCommonLayout = QGridLayout(commonFrame)

        self.autoRun = QCheckBox(commonFrame)
        PreferencesCommonLayout.addWidget(self.autoRun,0,0,1,2)
        self.autoFile = QLineEdit(commonFrame)
        PreferencesCommonLayout.addWidget(self.autoFile,0,2,1,8)
        self.chooseAuto = QPushButton(commonFrame)
        PreferencesCommonLayout.addWidget(self.chooseAuto,0,10,1,2)

        self.dirView = QTreeWidget(commonFrame)
        self.dirView.setColumnCount(1)
        self.dirView.headerItem().setText(0,_tr("Directories"))
        self.dirView.setMinimumHeight(60)
        self.dirView.setMaximumHeight(90)
        self.dirView.setToolTip(_tr("Choose directories with configuration files and add them to the list"))
        PreferencesCommonLayout.addWidget(self.dirView,1,0,1,12)

        self.addButton = QPushButton(commonFrame)
        PreferencesCommonLayout.addWidget(self.addButton,2,0,1,2)
        self.delButton = QPushButton(commonFrame)
        PreferencesCommonLayout.addWidget(self.delButton,2,2,1,2)

        ###
        self.line10 = QFrame(commonFrame)
        self.line10.setFrameShape(QFrame.HLine)
        self.line10.setFrameShadow(QFrame.Sunken)
        self.line10.setFrameShape(QFrame.HLine)
        PreferencesCommonLayout.addWidget(self.line10,3,0,1,12)

        ###
        self.showInList = QLabel(commonFrame)
        PreferencesCommonLayout.addWidget(self.showInList,4,0,1,3)
        self.at = QRadioButton(commonFrame)
        PreferencesCommonLayout.addWidget(self.at,4,3,1,2)
        self.srv = QRadioButton(commonFrame)
        PreferencesCommonLayout.addWidget(self.srv,4,5,1,2)
        self.bem = QRadioButton(commonFrame)
        PreferencesCommonLayout.addWidget(self.bem,4,7,1,2)
        self.iv = QRadioButton(commonFrame)
        PreferencesCommonLayout.addWidget(self.iv,4,9,1,2)
       
        ###
        self.appl = QCheckBox(commonFrame)
        PreferencesCommonLayout.addWidget(self.appl,5,3,1,2)
        self.custom = QCheckBox(commonFrame)
        PreferencesCommonLayout.addWidget(self.custom,5,5,1,2)
        self.examples = QCheckBox(commonFrame)
        PreferencesCommonLayout.addWidget(self.examples,5,7,1,2)
        self.nonAv = QCheckBox(commonFrame)
        PreferencesCommonLayout.addWidget(self.nonAv,5,9,1,2)
        
        ###
        self.line12 = QFrame(commonFrame)
        self.line12.setFrameShape(QFrame.HLine)
        self.line12.setFrameShadow(QFrame.Sunken)
        self.line12.setFrameShape(QFrame.HLine)
        PreferencesCommonLayout.addWidget(self.line12,6,0,1,12)
        
        ###
        self.autoStart = QLabel(commonFrame)
        PreferencesCommonLayout.addWidget(self.autoStart,7,0,1,4)
        self.autoGnome = QCheckBox(commonFrame)
        PreferencesCommonLayout.addWidget(self.autoGnome,7,4,1,4)
        self.autoKDE = QCheckBox(commonFrame)
        PreferencesCommonLayout.addWidget(self.autoKDE,7,8,1,4)

        ###
        self.line15 = QFrame(commonFrame)
        self.line15.setFrameShape(QFrame.HLine)
        self.line15.setFrameShadow(QFrame.Sunken)
        self.line15.setFrameShape(QFrame.HLine)
        PreferencesCommonLayout.addWidget(self.line15,8,0,1,12)
                        
        ###
        self.updateAppList = QLabel(commonFrame)
        PreferencesCommonLayout.addWidget(self.updateAppList,9,0,1,4)
        self.tmout = QLineEdit(commonFrame)
        self.tmout.setToolTip(_tr('Empty field means no update'))
        PreferencesCommonLayout.addWidget(self.tmout,9,4)
        self.secLabel1 = QLabel(commonFrame)
        PreferencesCommonLayout.addWidget(self.secLabel1,9,5)
        
        self.labelRunWhen = QLabel(commonFrame)
        PreferencesCommonLayout.addWidget(self.labelRunWhen,9,6,1,4,Qt.AlignRight)
        self.browseTmout = QLineEdit(commonFrame)
        self.browseTmout.setToolTip(_tr('Empty field means no update. Beware: Android/J2ME clients could fail to connect to anyRemote when browsing is in process'))
        PreferencesCommonLayout.addWidget(self.browseTmout,9,10)

        self.secLabel2 = QLabel(commonFrame)
        PreferencesCommonLayout.addWidget(self.secLabel2,9,11)

        #############################
        ### Server mode tab        
        #############################
        srvFrame = QFrame()
        self.tabs.addTab(srvFrame,_tr("Server Mode"))
        PreferencesSrvLayout = QGridLayout(srvFrame)
        
        self.connBT = QCheckBox(srvFrame)
        PreferencesSrvLayout.addWidget(self.connBT,0,0,1,3)
        self.dataBT = QLineEdit(srvFrame)
        PreferencesSrvLayout.addWidget(self.dataBT,0,3,1,4)
        
        self.connTCP = QCheckBox(srvFrame)
        PreferencesSrvLayout.addWidget(self.connTCP,1,0,1,3)
        self.dataTCP = QLineEdit(srvFrame)
        PreferencesSrvLayout.addWidget(self.dataTCP,1,3,1,4)
        
        self.connWEB = QCheckBox(srvFrame)
        PreferencesSrvLayout.addWidget(self.connWEB,2,0,1,3)
        self.dataWEB = QLineEdit(srvFrame)
        PreferencesSrvLayout.addWidget(self.dataWEB,2,3,1,4)

        self.connAVH = QCheckBox(srvFrame)
        PreferencesSrvLayout.addWidget(self.connAVH,3,0,1,3)
        
        self.connMAN = QCheckBox(srvFrame)
        PreferencesSrvLayout.addWidget(self.connMAN,4,0,1,3)
        self.dataMAN = QLineEdit(srvFrame)
        PreferencesSrvLayout.addWidget(self.dataMAN,4,3,1,4)
         
        ###
        self.line11 = QFrame(srvFrame)
        self.line11.setFrameShape(QFrame.HLine)
        self.line11.setFrameShadow(QFrame.Sunken)
        self.line11.setFrameShape(QFrame.HLine)
        PreferencesSrvLayout.addWidget(self.line11,5,0,1,7)

        ###
        self.jdirLabel = QLabel(srvFrame)
        PreferencesSrvLayout.addWidget(self.jdirLabel,6,0,1,2)
        self.j2meDir = QLineEdit(srvFrame)
        PreferencesSrvLayout.addWidget(self.j2meDir,6,2,1,4)
        self.jDir = QPushButton(srvFrame)
        PreferencesSrvLayout.addWidget(self.jDir,6,6)

        ###
        self.checkJ2ME = QCheckBox(srvFrame)
        PreferencesSrvLayout.addWidget(self.checkJ2ME,7,0,1,6)

        self.checkJ2MENow = QPushButton(srvFrame)
        PreferencesSrvLayout.addWidget(self.checkJ2MENow,7,5,1,2)
        
        self.downloadJ2MENow  = QPushButton(srvFrame)
        self.downloadJ2MENow.setToolTip(_tr('Files will be saved to $HOME/.anyRemote'))
        PreferencesSrvLayout.addWidget(self.downloadJ2MENow,8,5,1,2)
                
        self.empty = QLabel(srvFrame)
        self.empty.setText('')
        PreferencesSrvLayout.addWidget(self.empty,10,0)
        
        #############################
        ### AT mode tab        
        #############################
        atFrame = QFrame()
        self.tabs.addTab(atFrame,_tr("AT mode"))
        PreferencesATLayout = QGridLayout(atFrame)
        
        ###
        self.deviceATL = QLabel(atFrame)
        PreferencesATLayout.addWidget(self.deviceATL,0,0,1,2)
        self.deviceAT = QComboBox(atFrame)
        PreferencesATLayout.addWidget(self.deviceAT,0,2)

        ###
        self.auto_reconnect = QCheckBox(atFrame)
        PreferencesATLayout.addWidget(self.auto_reconnect,2,0,1,6)
                
        self.empty = QLabel(atFrame)
        self.empty.setText('')
        PreferencesATLayout.addWidget(self.empty,10,0)

        #############################
        ### Bemused tab        
        #############################
        bmFrame = QFrame()
        self.tabs.addTab(bmFrame,_tr("Bemused"))
        PreferencesBmLayout = QGridLayout(bmFrame)
        
        ###
        self.deviceBmL = QLabel(bmFrame)
        PreferencesBmLayout.addWidget(self.deviceBmL,0,0,1,2)
        self.deviceBm = QLineEdit(bmFrame)
        PreferencesBmLayout.addWidget(self.deviceBm,0,2)
        
        self.empty = QLabel(bmFrame)
        self.empty.setText('')
        PreferencesBmLayout.addWidget(self.empty,10,0)

        #############################
        ### iViewer tab        
        #############################
        ivFrame = QFrame()
        self.tabs.addTab(ivFrame,_tr("iViewer"))
        PreferencesIVLayout = QGridLayout(ivFrame)

        ###
        self.deviceIVL = QLabel(ivFrame)
        PreferencesIVLayout.addWidget(self.deviceIVL,0,0)
        self.deviceIV = QLineEdit(ivFrame)
        PreferencesIVLayout.addWidget(self.deviceIV,0,1,1,5)
         
        self.empty = QLabel(ivFrame)
        self.empty.setText('')
        PreferencesIVLayout.addWidget(self.empty,10,0)

        #############################
        self.cancel = QPushButton(self.centralWidget())
        prefsLayout.addWidget(self.cancel,6,3)
        self.ok = QPushButton(self.centralWidget())
        prefsLayout.addWidget(self.ok,6,4)

        self.languageChange()

        self.resize(QSize(500,300).expandedTo(self.minimumSizeHint()))
        #self.clearWState(Qt.WState_Polished)

        self.delButton.clicked.connect(self.delete_clicked)
        self.addButton.clicked.connect(self.add_clicked)
        self.jDir.clicked.connect(self.chooseJ2MEDir)
        self.cancel.clicked.connect(self.cancel_clicked)
        self.ok.clicked.connect(self.ok_clicked)
        
        self.checkJ2MENow.clicked.connect(self.checkJ2MEWeb)
        self.downloadJ2MENow.clicked.connect(self.uploadJ2MEWeb)
        
        self.nonAv.toggled.connect(self.filterChangedAction)
        self.appl.toggled.connect(self.filterChangedAction)
        self.custom.toggled.connect(self.filterChangedAction)
        self.examples.toggled.connect(self.filterChangedAction)
        self.at.toggled.connect(self.filterChangedAction)
        self.srv.toggled.connect(self.filterChangedAction)
        self.bem.toggled.connect(self.filterChangedAction)
        self.iv.toggled.connect(self.filterChangedAction)
       
        self.autoRun.toggled.connect(self.autoRunToggled)
        self.chooseAuto.clicked.connect(self.chooseAutoStart)
        
        self.connBT.toggled.connect(self.btToggled)
        self.connTCP.toggled.connect(self.tcpToggled)
        self.connWEB.toggled.connect(self.webToggled)
        self.connAVH.toggled.connect(self.avhToggled)
        self.connMAN.toggled.connect(self.manToggled)
        
        self.dataBT.textChanged.connect(self.btChangedAction)
        self.dataTCP.textChanged.connect(self.tcpChangedAction)
        self.dataWEB.textChanged.connect(self.webChangedAction)
        self.dataMAN.textChanged.connect(self.manChangedAction)
        
        self.deviceBm.textChanged.connect(self.deviceBmChangedAction)
        self.deviceIV.textChanged.connect(self.deviceIVChangedAction)

        self.tmout.textChanged.connect(self.updateAppChangedAction)
        self.browseTmout.textChanged.connect(self.browseDevChangedAction)
        
        self.updateAppChangedAction('')
        self.browseDevChangedAction('')

        self.makeGuiSetup()
        
    def makeGuiSetup(self):
        global bt_devices, cfgReader
        
        self.autoRun.setChecked (cfgReader.doAutoStart_)
        self.autoFile.setText   (cfgReader.autoStart_)

        self.appl.setChecked    (cfgReader.showApps_)
        self.custom.setChecked  (cfgReader.showCustom_)
        self.examples.setChecked(cfgReader.showExamples_)
        self.nonAv.setChecked   (cfgReader.showNonavail_)
        
        self.at.setChecked      (cfgReader.showAt_)
        self.srv.setChecked     (cfgReader.showSrv_)
        self.bem.setChecked     (cfgReader.showBm_)
        self.iv.setChecked      (cfgReader.showIView_)

        self.connBT.setChecked  (cfgReader.dvSrvUseBT_)
        self.connTCP.setChecked (cfgReader.dvSrvUseTCP_)
        self.connWEB.setChecked (cfgReader.dvSrvUseWEB_)
        self.connAVH.setChecked (cfgReader.dvSrvUseAVH_)
        self.connMAN.setChecked (cfgReader.dvSrvUseADV_)
            
        self.dataBT.setText (cfgReader.dvSrvBT_)
        self.dataTCP.setText(cfgReader.dvSrvTCP_)
        self.dataWEB.setText(cfgReader.dvSrvWEB_)
        self.dataMAN.setText(cfgReader.dvSrvADV_)
        
        self.setSensitivity()
        
        tm = ''
        if cfgReader.updateTmout_ > 0:
            tm = "%s" % (cfgReader.updateTmout_)
        self.tmout.setText(tm)

        tm = ''
        if cfgReader.browseTmout_ > 0:
            tm = "%s" % (cfgReader.browseTmout_)
        self.browseTmout.setText(tm)
        
        self.autoGnome.setChecked(os.path.exists(os.environ.get("HOME")+os.sep+'.config'+os.sep+'autostart'+os.sep+'kanyremote.desktop'))
        self.autoKDE.setChecked(os.path.exists(os.environ.get("HOME")+os.sep+'.kde'+os.sep+'Autostart'+os.sep+'kanyremote'))        
        
        self.auto_reconnect.setChecked(cfgReader.autoReconn_)
        
        if cfgReader.deviceAT_ != "":
            self.deviceAT.addItem(cfgReader.deviceAT_)
            self.deviceAT.setCurrentIndex(0)
            
        for k, v in bt_devices.items():

            row = bt_devices[k]
            v1 = row[0]
            v4 = row[3]
            
            # use channel 1 as default
            if v4 == "":
                v4 = "1"
            
            channels = v4.split(':')
            for ch in channels:
                if str(ch).isdigit():
                    crow = "rfcomm:"+v1+":"+ch
                    if cfgReader.deviceAT_ != crow:
                        self.deviceAT.addItem(crow)
        
        self.deviceBm.setText(cfgReader.deviceBm_)
        self.deviceIV.setText(cfgReader.deviceIV_)
        
        self.j2meDir.setText(cfgReader.j2meDir_)
        
        self.checkJ2ME.setChecked(cfgReader.checkJ2MEUpdate_)
        
        self.dirView.clear()
        for d in cfgReader.cfgDirs:
            if d != '':
                lvi = QTreeWidgetItem(self.dirView)
                lvi.setText(0,d)
        
        self.addDir      = ''
        self.dirsChanged = False
        
        self.deviceIVChangedAction(self)
        
    def uploadJ2MEWeb(self):
        global debug, jup, cfgReader
        
        try:
            if jup != None:
                if debug: print('Uploading already active. Skip request.')
                return
        except NameError:
            pass

        jup = JCUploader()
        jup.start()
        
        cfgReader.j2meDir_ = os.environ.get("HOME")+os.sep+'.anyRemote'
        try:
            self.j2meDir.setText(cfgReader.j2meDir_)
        except AttributeError:
            # OK. config window was not yet shown
            pass
                    
    def checkJ2MEWeb(self):
        jv = JCVerifier(True)
        jv.start()

    def closeEvent(self, event):
        self.cancel_clicked()

    def languageChange(self):
        self.setWindowTitle(_tr("Preferences"))
        
        self.showInList.setText(_tr("Show in list : "))
        self.jdirLabel.setText(_tr("Upload J2ME client from "))
        self.examples.setText(_tr("Examples"))
        self.appl.setText(_tr("Applications"))
        self.custom.setText(_tr("Custom Made"))
        self.srv.setText(_tr("Server-mode"))
        self.bem.setText(_tr("Bemused emulation"))
        self.at.setText(_tr("AT-mode"))
        self.iv.setText(_tr("iViewer"))
        self.nonAv.setText(_tr('Non-available'))
        self.updateAppList.setText(_tr("Update application list every"))
        self.secLabel1.setText(_tr("sec."))
        self.secLabel2.setText(_tr("sec."))
        
        self.connBT.setText(_tr("Use Bluetooth connection, channel"))
        self.connTCP.setText(_tr("Use TCP/IP connection, port"))
        self.connWEB.setText(_tr("Use Web Interace, port"))
        self.connAVH.setText(_tr("Advertise service through Avahi"))
        self.connMAN.setText(_tr("Specify options manually"))
        
        self.deviceATL.setText (_tr("Use connect string"))
        self.deviceBmL.setText (_tr("Use connect string"))
        self.deviceIVL.setText (_tr("Use connect string"))
        
        self.labelRunWhen.setText(_tr("Run device browser with timeout"))
        self.addButton.setText(_tr('Add'))
        self.delButton.setText(_tr('Delete'))
        self.jDir.setText(_tr('Choose'))
        self.auto_reconnect.setText(_tr("Auto reconnect"))
        self.autoGnome.setText(_tr("Gnome session"))
        self.autoStart.setText(_tr("Auto startup with"))
        self.autoKDE.setText(_tr("KDE session"))
        self.ok.setText(_tr('OK'))
        self.dirView.headerItem().setText(0,_tr("Directories"))
        self.autoRun.setText(_tr("Run on startup"))
        self.chooseAuto.setText(_tr("Choose"))
        self.cancel.setText(_tr('Cancel'))
        self.downloadJ2MENow.setText(_tr('Download J2ME client from Web'))
        self.checkJ2MENow.setText(_tr('Check J2ME client updates'))
        self.checkJ2ME.setText(_tr('Check J2ME client updates at start'))

    def delete_clicked(self):
        idx = 0
        while idx < self.dirView.topLevelItemCount():
            item = self.dirView.topLevelItem(idx)
            if self.dirView.isItemSelected(item):
                self.dirView.takeTopLevelItem(idx)
                continue
                
            idx = idx + 1 
             
        self.dirsChanged = True

    def add_clicked(self):
        f = QFileDialog().getExistingDirectory(None,_tr('Add'),self.addDir)
        self.addDir = os.path.dirname(str(f))
        
        add = True

        idx = 0
        item = self.dirView.topLevelItem(idx) 
        while item != None:
            if self.addDir == item.text(0):
                add = False
                break
            idx = idx+1 
            item = self.dirView.topLevelItem(idx)
        
        if add:
            lvi = QTreeWidgetItem(self.dirView)
            lvi.setText(0,str(f))

    def chooseAutoStart(self):
        global chooserWin
        self.chooseAuto.setEnabled(False)
        
        chooserWin = Chooser(self)
        chooserWin.show()

    def chooseJ2MEDir(self):
        f = QFileDialog().getExistingDirectory(None,_tr('Choose'),self.addDir)
        self.addDir = os.path.dirname(str(f))
        self.j2meDir.setText(self.addDir)

    def btChangedAction(self,a0):
        global cfgReader
        cfgReader.dvSrvBT_ = str(self.dataBT.text()).strip()
    
    def tcpChangedAction(self,a0):
        global cfgReader
        cfgReader.dvSrvTCP_ = str(self.dataTCP.text()).strip()
    
    def webChangedAction(self,a0):
        global cfgReader
        cfgReader.dvSrvWEB_ = str(self.dataWEB.text()).strip()
    
    def manChangedAction(self,a0):
        global cfgReader
        cfgReader.dvSrvADV_ = str(self.dataMAN.text()).strip()
    
    def deviceBmChangedAction(self,a0):
        global cfgReader
        cfgReader.deviceBm_ = str(self.deviceBm.text()).strip()
    
    def deviceIVChangedAction(self,a0):
        global cfgReader
        cfgReader.deviceIV_ = str(self.deviceIV.text()).strip()

    def updateAppChangedAction(self,a0):
        self.updateAppList.setEnabled((str(self.tmout.text()).strip() != ''))
        self.secLabel1.setEnabled    ((str(self.tmout.text()).strip() != ''))
    
    def browseDevChangedAction(self,a0):
        self.labelRunWhen.setEnabled((str(self.browseTmout.text()).strip() != ''))
        self.secLabel2.setEnabled   ((str(self.browseTmout.text()).strip() != ''))

    def cancel_clicked(self):
        self.hide()
        self.makeGuiSetup()
        sendMainWindow(20005,False)

    def ok_clicked(self):
        global cfgReader
        
        self.setAutoStartup("KDE",   self.autoKDE.isChecked())
        self.setAutoStartup("Gnome", self.autoGnome.isChecked())
        
        cfgReader.autoStart_    = str(self.autoFile.text()).strip()
        cfgReader.doAutoStart_  = self.autoRun.isChecked()
        
        cfgReader.showApps_     = self.appl.isChecked()
        cfgReader.showCustom_   = self.custom.isChecked()
        cfgReader.showExamples_ = self.examples.isChecked()
        cfgReader.showNonavail_ = self.nonAv.isChecked()
        cfgReader.showAt_       = self.at.isChecked()
        cfgReader.showSrv_      = self.srv.isChecked()
        cfgReader.showBm_       = self.bem.isChecked()
        cfgReader.showIView_    = self.iv.isChecked()

        cfgReader.autoReconn_   = self.auto_reconnect.isChecked()
        cfgReader.deviceAT_     = str(self.deviceAT.currentText()).strip()
        cfgReader.deviceBm_     = str(self.deviceBm.text()).strip()
        cfgReader.deviceIV_     = str(self.deviceIV.text()).strip()
        cfgReader.j2meDir_      = str(self.j2meDir.text()).strip()
        cfgReader.checkJ2MEUpdate_ = self.checkJ2ME.isChecked()
        
        tm = str(self.tmout.text()).strip()
        if tm == '':
            tm = '-1'
        if cfgReader.updateTmout_ != int(tm):
            cfgReader.updateTmout_ = int(tm)
            if cfgReader.updateTmout_ < 0:
                cfgReader.updateTmout_ = -1
            
            # start/stop browser
            sendMainWindow(20010,'')

        tm = str(self.browseTmout.text()).strip()
        if tm == '':
            tm = '-1'
        if cfgReader.browseTmout_ != int(tm):
            cfgReader.browseTmout_ = int(tm)
            if cfgReader.browseTmout_ <= 0:
                cfgReader.browseTmout_ = -1
            
            # start/stop updater
            sendMainWindow(20011,'')
        
        cfgReader.cfgDirs = []
        
        idx = 0
        item = self.dirView.topLevelItem(idx)
        while item != None:
            cfgReader.cfgDirs.append(str(item.text(0)))
            idx = idx+1 
            item = self.dirView.topLevelItem(idx)
        
        ret = QMessageBox.Ok
        if cfgReader.cfgDirs == []:
            ret = QMessageBox.warning(self, "kAnyRemote",
                         _tr("There is no item in the list !\nkAnyRemote will not be able to manage any software !"),QMessageBox.Ok,QMessageBox.Cancel);

        if ret == QMessageBox.Ok:
            cfgReader.saveConfig() 
            self.hide()
            
            sendMainWindow(20005,self.dirsChanged)
            self.dirsChanged = False

    def filterChangedAction(self, w):
            self.dirsChanged = True

    def setSensitivity(self):
        self.dataBT.setEnabled(self.connBT.isChecked())
        self.dataTCP.setEnabled(self.connTCP.isChecked())
        self.dataWEB.setEnabled(self.connWEB.isChecked())
        self.connAVH.setEnabled(self.connTCP.isChecked() or self.connWEB.isChecked())
        self.dataMAN.setEnabled(self.connMAN.isChecked())

    def manToggled(self, w):
        global cfgReader
        cfgReader.dvSrvUseADV_ = w
        if w:
            self.connBT.setChecked(False)
            self.connTCP.setChecked(False)
            self.connWEB.setChecked(False)
            self.connAVH.setChecked(False)
            
            cfgReader.dvSrvUseBT_  = False
            cfgReader.dvSrvUseTCP_ = False
            cfgReader.dvSrvUseWEB_ = False
            cfgReader.dvSrvUseAVH_ = False

            if str(self.dataMAN.text()).strip() == '':
                self.dataMAN.setText("-log -s bluetooth:19,tcp:5197,web:5080,avahi")
        self.setSensitivity()

    def btToggled(self, w):
        global cfgReader
        cfgReader.dvSrvUseBT_ = w
        if w:
            self.connMAN.setChecked(False)
            cfgReader.dvSrvUseADV_ = False
            
            if str(self.dataBT.text()).strip() == '':
                self.dataBT.setText("19")
        self.setSensitivity()
        
    def tcpToggled(self, w):
        global cfgReader
        cfgReader.dvSrvUseTCP_ = w
        if w:
            self.connMAN.setChecked(False)
            cfgReader.dvSrvUseADV_ = False
            
            if str(self.dataTCP.text()).strip() == '':
              self.dataTCP.setText("5197")
        self.setSensitivity()
         
    def webToggled(self, w):
        global cfgReader
        cfgReader.dvSrvUseWEB_ = w
        if w:
            self.connMAN.setChecked(False)
            cfgReader.dvSrvUseADV_ = False
            
            if str(self.dataWEB.text()).strip() == '':
              self.dataWEB.setText("5080")
        self.setSensitivity()

    def avhToggled(self, w):
        global cfgReader
        cfgReader.dvSrvUseAVH_ = w
        if w:
            self.connMAN.setChecked(False)
            cfgReader.dvSrvUseADV_ = False
 
        self.setSensitivity()

    def autoRunToggled(self, w):
        self.autoFile.setEnabled(self.autoRun.isChecked())
        self.chooseAuto.setEnabled(self.autoRun.isChecked())

    def setAutoStartup(self, wm, isAuto):
        if wm == 'KDE':
            autopath = os.environ.get("HOME")+os.sep+'.kde'+os.sep+'Autostart'+os.sep+'ganyremote'
            if isAuto:
                if not os.path.exists(autopath):
                    os.system('ln -s `which ganyremote` '+autopath)
            else:
                os.system('rm -f '+autopath)

        elif wm == 'Gnome':  # will work for gnome > 2.14 ?
            cfgpath  = os.environ.get("HOME")+os.sep+'.config'
            autopath = cfgpath+os.sep+'autostart'
            autorun  = autopath+os.sep+'ganyremote.desktop'

            if not os.path.isdir(cfgpath):
                os.mkdir(cfgpath)
            if not os.path.isdir(autopath):
                os.mkdir(autopath)
            if isAuto:
                if not os.path.isfile(autorun):
                    f=open(autorun, 'w')
                    if f:
                        f.write('[Desktop Entry]\n')
                        f.write('Encoding=UTF-8\n')
                        f.write('Name=gAnyRemote\n')
                        f.write('Exec=ganyremote\n')
                        f.write('Icon=ganyremote\n')
                        f.write('Type=Application\n')
                        f.write('Comment=Remote control through bluetooth or IR connection\n')
                        f.write('GenericName=Remote control through bluetoth or IR connection\n')
                        f.write('Categories=Utility;\n')
                        f.close()
            else:
                os.system('rm -f '+autorun)

    def customEvent(self,event):
        if event.type() == 31000:
            self.autoFile.setText(event.getData())
            self.autoFile.setEnabled(True)
            self.chooseAuto.setEnabled(True)
        elif event.type() == 32000:
            self.chooseAuto.setEnabled(True)

###############################################################################        
#
#        Handle Return/Enter in QLineEdit
#
###############################################################################        

class QLineEditEx(QLineEdit):

    def __init__(self,parent = None,toplevel = None):
        QLineEdit.__init__(self,parent)
        self.top = toplevel
        
    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Enter or event.key() == Qt.Key_Return:
            self.top.exec_clicked()
        else:
            QLineEdit.keyPressEvent(self,event)

###############################################################################        
#
#        Custom Event
#
###############################################################################        

class QEventEx(QEvent):

    def __init__(self, eid, data=None):
        QEvent.__init__(self,eid)
        self.data = data

    def getData(self):
        return self.data

###############################################################################        
#
#        kAnyRemote
#
###############################################################################        

class kAnyRemote(QMainWindow):
    def __init__(self,cfgSet):
        QMainWindow.__init__(self)

        global app, debug, guiMode, cfgReader
        
        self.statusBar()
        
        row = 3
        if debug:
            row = 4

        if guiMode == 'simple':
            row = row - 1

        self.setCentralWidget(QWidget(self))
        kAnyRemoteLayout = QGridLayout(self.centralWidget())
        kAnyRemoteLayout.setContentsMargins(6,6,6,6)
        kAnyRemoteLayout.setSpacing(6)

        self.stop_button  = QPushButton(loadIcon("dialog-close","stop"), _tr("Stop"),  self.centralWidget())
        self.run_button   = QPushButton(loadIcon("system-run",  "run"),  _tr("Start"), self.centralWidget())
        
        kAnyRemoteLayout.addWidget(self.run_button, row,0,1,4)
        kAnyRemoteLayout.addWidget(self.stop_button,row,4,1,4)

        self.listView1 = QTreeWidget(self.centralWidget())
        self.listView1.setColumnCount(5)
        self.listView1.headerItem().setText(0,_tr("Application")+"    ")
        self.listView1.headerItem().setText(1,_tr("Status")+"         ")
        self.listView1.headerItem().setText(2,_tr("Mode")+"    ")
        self.listView1.headerItem().setText(3,_tr("Type")+"    ")
        self.listView1.headerItem().setText(4,_tr("F")+"    ")
        
        self.listView1.header().setSectionResizeMode(0,QHeaderView.ResizeToContents)
        self.listView1.header().setSectionResizeMode(1,QHeaderView.ResizeToContents)
        self.listView1.header().setSectionResizeMode(2,QHeaderView.ResizeToContents)
        self.listView1.header().setSectionResizeMode(3,QHeaderView.Stretch)
        
        self.listView1.setSelectionMode(QAbstractItemView.SingleSelection)
        
        self.listView1.hideColumn(4)
        self.listView1.setSortingEnabled(True)
        self.listView1.sortByColumn(0,Qt.AscendingOrder)

        self.listView1.itemClicked.connect(self.listClicked)
        self.listView1.itemDoubleClicked.connect(self.listDClicked)
        self.listView1.currentItemChanged.connect(self.cursorMoved)

        kAnyRemoteLayout.addWidget(self.listView1,0,0,1,8)
        
        self.details = QTreeWidget(self.centralWidget())
        self.details.setHeaderHidden(True)
        self.details.setColumnCount(1)

        self.details.setSizePolicy(QSizePolicy.Minimum,QSizePolicy.Maximum)
        self.details.setRootIsDecorated(True)
        self.details.setSelectionMode(QAbstractItemView.NoSelection)
        self.details.setFrameStyle(QFrame.NoFrame)

        p = QPalette(self.details.palette())
        p.setColor(QPalette.Base, QApplication.palette().color(QPalette.Window))
        self.details.setPalette(p)
        
        self.details.itemExpanded.connect(self.detailExpanded)
        self.details.itemCollapsed.connect(self.detailCollapsed)
        
        kAnyRemoteLayout.addWidget(self.details,1,0,1,8)
  
        self.detailsHead = QTreeWidgetItem(self.details)
        self.detailsHead.setText(0,_tr("Details"))       
        self.detailsHead.setExpanded(False)
        self.detailsItem = QTreeWidgetItem(self.detailsHead)
        self.detailsItem.setText(0,'') 
        
        h = self.details.visualItemRect(self.detailsHead).height()
        if h == 0: h = 18
        self.details.setMinimumHeight(h)
        self.details.setMaximumHeight(h)

        if guiMode == 'expert':
            self.cfgFile = QLineEdit(self.centralWidget())
            kAnyRemoteLayout.addWidget(self.cfgFile,2,0,1,6)
            self.choose = QPushButton(loadIcon("document-open","fileopen"), _tr("Choose"), self.centralWidget())
            kAnyRemoteLayout.addWidget(self.choose,2,6,1,2)

        if debug:
            self.command = QLineEditEx(self.centralWidget(),self)
            kAnyRemoteLayout.addWidget(self.command,3,0,1,6)
            self.execcmd = QPushButton(loadIcon("system-run","exec"), _tr("Execute Command"), self.centralWidget())
            kAnyRemoteLayout.addWidget(self.execcmd,3,6,1,2)
            self.execcmd.setEnabled(True)


        # File menu
        self.fileExitAction = QAction(loadIcon("application-exit","exit"),     _tr("Quit"), self)
        self.edit_file      = QAction(loadIcon("document-edit",   "edit"),     _tr("Edit"), self)
        self.start          = QAction(loadIcon("system-run",      "run"),      _tr("Start"),self)
        self.stop           = QAction(loadIcon("dialog-close",    "stop"),     _tr("Stop"), self)
        self.update         = QAction(loadIcon("reload", "appointment-recurring"), _tr("Update Status"), self)
        self.close_window   = QAction(loadIcon("edit-delete", "fileclose"),    _tr("Close Window"), self)
        
        # Edit menu
        self.save_prefs = QAction(loadIcon("document-save","filesave"),   _tr("Save"),               self)
        self.properties = QAction(loadIcon("configure","configure"),      _tr("Preferences"),             self)
        self.chkCfg     = QAction(loadIcon("dialog-ok-apply","button_ok"),_tr("Check Configuration"),self)
        self.dbrowser   = QAction(loadIcon("zoom-in","filefind"),         _tr("Device Browser"),     self)
        
        # Help menu
        self.helpContentsAction = QAction(loadIcon("help-contents", "help"), _tr("Help"),  self)
        self.helpAboutAction    = QAction(loadIcon("help-about",    "rating"),  _tr("About"), self)
        
        self.start.setEnabled(False)

        self.Menu = self.menuBar()

        self.fileMenu = self.Menu.addMenu(_tr("File"));
        self.fileMenu.addSeparator()
        self.fileMenu.addAction(self.edit_file)
        self.fileMenu.addAction(self.start)
        self.fileMenu.addAction(self.stop)
        self.fileMenu.addAction(self.update)
        self.fileMenu.addAction(self.close_window)
        self.fileMenu.addAction(self.fileExitAction)

        self.cfgMenu = self.Menu.addMenu(_tr("Setup"))
        self.cfgMenu.addAction(self.dbrowser)
        self.cfgMenu.addAction(self.properties)
        self.cfgMenu.addAction(self.chkCfg)
        self.cfgMenu.addAction(self.save_prefs)

        self.helpMenu = self.Menu.addMenu(_tr("Help"))
        self.helpMenu.addAction(self.helpAboutAction)
        self.helpMenu.addAction(self.helpContentsAction)

        self.Menu.addSeparator()

        self.languageChange()

        self.resize(QSize(370,300).expandedTo(self.minimumSizeHint()))

        self.fileExitAction.triggered.connect(self.fileExit)
        self.edit_file.triggered.connect(self.edit_file_activated)
        self.start.triggered.connect(self.runAction)
        self.stop.triggered.connect(self.stopAction)
        self.update.triggered.connect(self.rescan_dirs_clicked)
        self.close_window.triggered.connect(self.close_window_clicked)
        
        self.dbrowser.triggered.connect(self.showBrowser)
        self.save_prefs.triggered.connect(self.saveCfg)
        self.properties.triggered.connect(self.showConfDialog)
        self.chkCfg.triggered.connect(self.showChkWin)

        self.helpContentsAction.triggered.connect(self.helpAction)
        self.helpAboutAction.triggered.connect(self.helpAbout)
        
        if guiMode == 'expert':
            self.choose.clicked.connect(self.choose_clicked)
        self.stop_button.clicked.connect(self.stopAction)
        self.run_button.clicked.connect(self.runAction)
        self.destroyed.connect(self.close_window_clicked)
        
        h = 500
        if debug:
            h = 470
            self.execcmd.clicked.connect(self.exec_clicked)
        
        self.resize(QSize(470,h).expandedTo(self.minimumSizeHint()))
        self.win_width = 470
        
        self.stop.setEnabled(False)
        self.stop_button.setEnabled(False)
        
        if guiMode == 'expert':
            self.cfgFile.setText(cfgReader.lastCfgFile_)

        self.cfgWin        = None
        self.frontEnd      = None
        self.statusUpdater = None                
        
        # will send event to start (if needed) StatusUpdater 
        #self.populateCfgFiles()        
        
        # will start device browser (if needed)
        startBtComm()
        
        self.setStatusStopped()
        
        if not cfgSet or cfgReader.j2meDir_ == '':
            self.showWizard(cfgSet,cfgReader.j2meDir_)
    
    def languageChange(self):
        global debug, guiMode
    
        self.setWindowTitle("kAnyRemote")
        self.listView1.headerItem().setText(0,_tr("Application"))
        self.listView1.headerItem().setText(1,_tr("Status"))
        self.listView1.headerItem().setText(2,_tr("Mode"))
        self.listView1.headerItem().setText(3,_tr("Type"))
        if guiMode == 'expert':
            self.choose.setText(_tr('Choose'))
        if debug:
            self.execcmd.setText(_tr("Execute Command"))
        
        self.close_window.setText(_tr("Close Window"))
        self.stop_button.setText(_tr("Stop"))
        self.run_button.setText(_tr("Start"))
        self.fileExitAction.setText(_tr("Quit"))
        
        self.helpContentsAction.setText(_tr("Help"))
        
        self.helpAboutAction.setText(_tr("About"))
        
        self.edit_file.setText(_tr("Edit"))
        self.edit_file.setToolTip(_tr("Edit configuration file"))
        self.start.setText(_tr("Start"))
        self.stop.setText(_tr("Stop"))
        self.stop.setToolTip(_tr("Stop anyRemote"))
        self.save_prefs.setText(_tr("Save"))
        self.properties.setText(_tr("Preferences"))
        self.chkCfg.setText(_tr("Check Configuration"))
        self.dbrowser.setText(_tr("Device Browser"))
        
        self.fileMenu.menuAction().setText(_tr("File"))
        self.cfgMenu.menuAction().setText(_tr("Setup"))
        self.helpMenu.menuAction().setText(_tr("Help"))
  
    def showWizard(self, cfgdirs, jclient):
        if not cfgdirs:
            ret = QMessageBox.information(self, "kAnyRemote",
                     _tr("This is the first time kAnyRemote runs.\nPlease specify directories with anyRemote configuration files."),QMessageBox.Ok);
            self.showConfDialog()
        
        if jclient == '': 
            ret  = yesno(self,_tr('Would You like to download J2ME client from Web ?'))
            if ret:
                if not self.cfgWin:
                    self.cfgWin = Preferences();
                self.cfgWin.uploadJ2MEWeb()

    def showConfDialog(self):
        if not self.cfgWin:
            self.cfgWin = Preferences();
        self.cfgWin.show()
        self.properties.setEnabled(False)

    def showChkWin(self):
        showChkCfgWin()

    def saveCfg(self):
        global cfgReader
        cfgReader.saveConfig()
    
    def showBrowser(self):
        showBrowserWin()

    def getStatus(self,isRun,status):
       if status == AR_MANAGED and isRun == 'NOK':
           status = AR_AVAIL
       elif status == AR_AVAIL and isRun == 'OK':
           status = AR_RUNNING
       elif status == AR_RUNNING and isRun == 'NOK':
           status = AR_AVAIL
 
       return status
 
    def addRow(self,rowData):
        #[aName, pbuf, isRun, status, aMode, aType, idx]
       lvi = QTreeWidgetItem(self.listView1)

       ic = loadAppIcon(rowData[1])

       lvi.setIcon(0,ic)
       lvi.setText(0,rowData[0])       
       lvi.setText(1,self.getStatus(rowData[2],rowData[3]))
       lvi.setText(2,rowData[4])
       lvi.setText(3,rowData[5])
       lvi.setText(4,str(rowData[6]))
  
    def listClicked(self, listItem, column):
        global debug, cfgReader, appData, guiMode
        try:
            cfgReader.lastCfgFile_ = appData[int(str(listItem.text(4)))][5]
            desc = appData[int(str(listItem.text(4)))][9]
            self.setDetails(desc)

            if guiMode == 'expert':
                self.cfgFile.setText(cfgReader.lastCfgFile_)
        except AttributeError:
            if debug: print('kAnyRemote.listClicked: AttributeError')
        

    def listDClicked(self, listItem, column):
        self.listClicked(listItem, column)
        self.runAction()

    def cursorMoved(self,listItem,previous):
        global debug, cfgReader, appData, guiMode
        try:
            cfgReader.lastCfgFile_ = appData[int(str(listItem.text(4)))][5]
            desc = appData[int(str(listItem.text(4)))][9]
            self.setDetails(desc)
        
            if guiMode == 'expert':
                 self.cfgFile.setText(cfgReader.lastCfgFile_)
        except (AttributeError, KeyError):
            if debug: print('kAnyRemote.cursorMoved: an Error')
        
    def fileExit(self):
        global app, quitFlag, debug, jup
        
        if debug: print("kAnyRemote.fileExit")
        quitFlag = True
        
        closeDetailsWin()
        closeBrowserWin()
        closeChkCfgWin()
        closeChooserWin()
        
        self.stopUpdater()
        stopFrontend()
        stopBtComm()
        stopJCUploader()
        stopPBar()
        
        time.sleep(0.5)
	
        app.quit()

    # open web browser on docs page if found ?
    def helpAction(self):
    
        app = ''
        apps = ['xdg-open', 'firefox', 'konqueror', 'chromium']
        for test in apps:
            isInst = isInstalled(test) 
            if isInst == 'OK':
                app = test
        if app == '':
            errorBox(self,_tr('Can not find browser to show help !'))
            return

        docpath = ''
        docDirs = ['/usr/share/doc/anyremote*/*html', 
                   '/usr/share/anyremote*/*html', 
                   '/usr/share/doc/anyremote-doc', 
                   '/usr/local/share/doc/anyremote*/*html', 
                   '/usr/local/share/anyremote*/*html']

        for docDir in docDirs:
            dirv = glob.glob(docDir)
            if len(dirv) > 0 and os.path.isdir(dirv[0]):
                docpath = dirv[0]
                break
        if docpath == '':
            errorBox(self,_tr('Can not find documentation !'))
            return
        
        cmd = app + ' ' + docpath + os.sep + 'k-shots.html &'
        if debug: print(cmd)
        os.system(cmd)

    def helpAbout(self):
        global app
        a = QMessageBox(QMessageBox.Information, "About kAnyRemote", "<h2>kAnyRemote v8.0</h2>", QMessageBox.Ok, self) 
        a.setIconPixmap(getPixmap("kanyremote"))
        a.setInformativeText("<center>Copyright 2007-2019<p>Mikhail Fedotov<p><a href=\"http://anyremote.sf.net\">http://anyremote.sf.net</a></center>");
        a.setDetailedText("KDE front-end for anyRemote (bluetooth remote control for PC).\nThe code is licensed under the GNU Public licence.")
        a.show()

    def edit_file_activated(self):
        global cfgReader
        if cfgReader.lastCfgFile_:
            self.eWin = EditWin(cfgReader.lastCfgFile_,self)
            self.eWin.show()

    def choose_clicked(self):
        fileName = QFileDialog.getOpenFileName(None,"", "");
        self.cfgFile.setText(fileName)

    def rescan_dirs_clicked(self):
        self.listView1.clear()
        self.populateCfgFiles()
    
    def closeEvent(self, event):
        event.ignore()
        self.close_window_clicked()
    
    def close_window_clicked(self):
        global systray
        if systray:
            self.hide()
        else:
            self.fileExit()

    def stopAction(self):
        global debug
        if debug: print("kAnyRemote.stopAction()")

        self.saveCfg()
        
        # To avoid socket.error: (98, 'Address already in use') 
        # it needs to close client socket first
        killAnyRemote()

        self.setStatusStopped()
   
    def minMaxAction(self):
        self.setVisible(not self.isVisible())

    def exec_clicked(self):
        global cmdToSend, debug
       
        if debug:
            print('Add to queue ' + self.command.text())
            cmdToSend = self.command.text()

    def runAction(self):
        global debug, cfgReader

        if debug: print("kAnyRemote.runAction()")
        
        self.saveCfg()
        
        self.setStatusStopped()
        
        cfile = cfgReader.lastCfgFile_
        if guiMode == 'expert':
            cfile = self.cfgFile.text()

        startAnyRemote(cfile)        
                            
    def restartUpdater(self):
        self.stopUpdater()
        self.startUpdater()

    def startUpdater(self):
        global cfgReader
        if cfgReader.updateTmout_ > 0:
            self.statusUpdater = StatusUpdater(cfgReader.updateTmout_)
            self.statusUpdater.start()

    def stopUpdater(self):
        try:
            self.statusUpdater.stop()
        except (AttributeError, NameError):
            pass
        time.sleep(0.5)
        self.statusUpdater = None

    def showEvent(self, event):
        global debug
        if debug: print('showEvent')
        self.populateCfgFiles()

    def resizeEvent(self, event):
        w = self.width() - self.win_width
        
        if w > -5 and w < 5: # resize is too small
           return
        
        self.win_width = self.width()  
        desc = str(self.detailsItem.text(0)).replace('\n',' ')
        self.setDetails(desc)

    def populateCfgFiles(self):
        global debug, cfgFileReaderFlag
        if debug: print('populateCfgFiles')
        
        try:
            if cfgFileReaderFlag:
                return
        except NameError:
            pass
        
        self.stopUpdater()
        cfgFileReader = CfgFileReader()
        cfgFileReader.start()

    def autoStartBackend(self):
        global cfgReader, frontEnd, cfgFileReaderFlag

        if cfgReader.doAutoStart_ and cfgReader.autoStart_ != '':

            if not frontEnd or not frontEnd.ready() or cfgFileReaderFlag:
                # respawn timer
                QTimer.singleShot(200, self.autoStartBackend)
                return 

            self.setStatusStopped()
            startAnyRemote(cfgReader.autoStart_)
        
    def setTempStatus(self, text):
        self.statusBar().clearMessage()
        self.statusBar().showMessage(text)

    def setStatustext(self,text):
        self.statusValue = text
        self.setTempStatus(text)
        
    def setStatusStopped(self):
        setTrayIcon("kanyremote_off")
        self.setStatustext(_tr('anyRemote stopped'))
        self.stop.setEnabled(False)
        self.start.setEnabled(True)
        self.stop_button.setEnabled(False)
        self.run_button.setEnabled(True)

    def setStatusDisconnected(self):
        global cfgReader
        
        setTrayIcon("kanyremote_light")
        self.setStatustext(_tr('Ready to connect on')+' '+cfgReader.getConnectStatusString())
        self.stop.setEnabled(True)
        self.start.setEnabled(False)
        self.stop_button.setEnabled(True)
        self.run_button.setEnabled(False)

    def setStatusConnected(self):
        global cfgReader
        
        setTrayIcon("kanyremote_small")
        msg = _tr('Connected to phone')
        if cfgReader.dvSrvUseWEB_:
            msg = _tr('Connected') 
        self.setStatustext(msg)
        self.stop.setEnabled(True)
        self.start.setEnabled(False)
        self.stop_button.setEnabled(True)
        self.run_button.setEnabled(False)
        
    def listUpdate(self, data):
        #print('listUpdate',data)
        st = self.getStatus(data[2],data[1])
        
        idx = 0
        item = self.listView1.topLevelItem(idx) 
        while item != None:
            try:
                if int(item.text(4)) == data[0]:
                    item.setText(1,st)
                    break
            except ValueError:
                pass
            idx = idx+1 
            item = self.listView1.topLevelItem(idx)

    def sliceString(self, string, font, width):

        fm =  QFontMetrics(font)
        sliced = ''
        line   = ''
        
        wordList = string.split(' ')
        for word in wordList:
            line1 = line+' '+word
            if fm.width(line1) > width:
                if len(sliced) != 0:
                    sliced = sliced + '\n'
                sliced = sliced + line
                line = word
            else:
                line = line1
                
        return sliced + '\n' + line

    def setDetails(self, desc):
        slicedString = self.sliceString(desc, self.details.font(), self.width()-80)
        self.detailsItem.setText(0,slicedString.lstrip())
        cnt = slicedString.count('\n')
        
        if self.detailsHead.isExpanded():
            h = self.details.visualItemRect(self.detailsHead).height()*(cnt+2)
            if h == 0: h = 18*(cnt+2)
            self.details.setMinimumHeight(h)
            self.details.setMaximumHeight(h)

    def selectLastInList(self):
      global cfgReader
      idxLast = None
      for k, v in appData.items():
        if v[5] == cfgReader.lastCfgFile_:
          idxLast = k
          break
      idx = 0
      item = self.listView1.topLevelItem(idx)
      while item != None:
        try:
          if int(str(item.text(4))) == idxLast:
            self.listView1.setCurrentItem(item)
            desc = appData[int(str(item.text(4)))][9]
            self.setDetails(desc)
            return
        except ValueError:
          pass   
        idx = idx+1 
        item = self.listView1.topLevelItem(idx)
    
    def translateMsg(self, data):
        global debug
        if debug: print('translateMsg '+data)

        if data == '':
           return
        elif data == 'Exiting':
           self.setStatusStopped()
           return
        elif data == 'Connected':
           self.setStatusConnected()
           return
        elif data == 'Disconnected':
           self.setStatusDisconnected()
           return
        if data == '(Init)':
           msg = 'anyRemote initialized'
        else:
           if debug: print('translateMsg BlinkThread')
           
           # Just key press
           setTrayIcon("kanyremote_flash") 
           QTimer.singleShot(200, resetIcon)  
 
           if debug:
               msg = data
           else:
               return
        
        self.setStatustext(msg)

    def customEvent(self,event):
      global appData, debug
      
      #print('customEvent '+str(event.type()))
      
      if event.type() == 20000:
        self.setStatusStopped()
      elif event.type() == 20003:
        cfgFile = getResult('ps aux |grep anyremote|grep cfg|awk \'{n=split($0,arr);print arr[n-2];}\'', 'frontend')
        if cfgFile != '':
          for k, v in appData.items():
            if appData[k][5] == cfgFile:
              appData[k][6] = AR_MANAGED
              self.listUpdate([k,appData[k][6],'OK'])
              break
        self.setStatusDisconnected()
      elif event.type() == 20001:
        setTrayIcon("kanyremote_flash")
      elif event.type() == 20002:
        setTrayIcon("kanyremote_small")
      elif event.type() == 20005:
        self.cfgWinClosed(event.getData())
      elif event.type() == 20006:
        self.listUpdate(event.getData())
      elif event.type() == 20008:
        self.dcopHandleSend(event.getData())
      elif event.type() == 20009:
        self.dcopHandleGetMode()
      elif event.type() == 20010:
        self.restartUpdater()
      elif event.type() == 20011:
        restartBtComm()
      elif event.type() == 20012:
        for k, v in appData.items():
          if appData[k][6] == AR_MANAGED:
            aRun = appData[k][3]
            isRun = ''
            if aRun != '':
              isRun  = getResult(aRun,'main')
              if isRun == 'OK':
                appData[k][6] = AR_RUNNING
              else:
                appData[k][6] = AR_AVAIL
            else:
              appData[k][6] = '' 
            self.listUpdate([k,appData[k][6],isRun])
      elif event.type() == 20014:
        ret = yesno(self,_tr("New version of J2ME client is available. Would You like to download it ?"))
        if ret :
          if not self.cfgWin:
            self.cfgWin = Preferences();
          self.cfgWin.uploadJ2MEWeb() 
      elif event.type() == 20015:
        infoBox(self,event.getData())
      elif event.type() == 20016:
        errorBox(self,event.getData())
      elif event.type() == AR_CODE_LOADED:
        value = int(event.getData())
        if value >= 0:
          self.setTempStatus(str(value))
        else:
          self.setStatustext(self.statusValue)
          self.listView1.clear()
          t1 = datetime.datetime.now()
          for k, v in appData.items():
            self.addRow([appData[k][0],appData[k][1],appData[k][4],appData[k][6],appData[k][7],appData[k][8],k])
          t2 = datetime.datetime.now()
          if debug: print("ELAPSED ",(t2-t1))
          self.selectLastInList()
          self.restartUpdater()
      elif event.type() == 20018:
        self.stopProgressBar()
      elif event.type() == 20019:
        self.startProgressBar(event.getData())
      elif event.type() == 20020:
        self.showDevDetails(event.getData())
      elif event.type() == 20021:
        self.writeProgressBar(event.getData())
      elif event.type() == 20022:
        self.listView1.clear()
      elif event.type() == 29999:
        self.fileExit()
      else:
        self.translateMsg(str(event.getData()))
           
    def pushTrayIcon(self):
        if self.isShown == 1:
            self.hide()
            self.isShown = 0
        else:
            self.show()
            self.isShown = 1
            
    def cfgWinClosed(self, isChanged):
        self.properties.setEnabled(True)
        if isChanged:
            self.populateCfgFiles()
            
    def dcopHandleSend(self, cmd):
        global cmdToSend
        if debug: print('Add to queue ' + cmd)
        cmdToSend = cmd
    
    def detailExpanded(self, item):
        cnt = str(self.detailsItem.text(0)).count('\n')
        h = self.details.visualItemRect(self.detailsHead).height()*(cnt+2)
        if h == 0: h = 18*(cnt+2)
        self.details.setMinimumHeight(h)
        self.details.setMaximumHeight(h)

    def detailCollapsed(self, item):
        h = self.details.visualItemRect(self.detailsHead).height()
        if h == 0: h = 18
        self.details.setMinimumHeight(h)
        self.details.setMaximumHeight(h)
    
    def  stopProgressBar(self):
        global pbar, pbarTimer,debug
    
        if debug: print('stopProgressBar')

        try:
            pbarTimer.stop()
        except AttributeError:
            pass
        
        try:
            pbar.hide()
            pbar.cancel()
            pbar = None
        except (AttributeError, NameError):
            pass
            
    def startProgressBar(self,text):
        global debug, pbar, pbarTimer
        
        if debug: print('startProgressBar')
    
        pbar = PBar(text,100)
        timerPBar()

    def writeProgressBar(self,text):
        global pbar

        try:
            pbar.setLabelText(text)
        except (AttributeError, NameError):
            pass

    def showDevDetails(self,dataList):
        global detailsWin, cfgReader
        detailsWin = DeviceDetail(dataList[0], dataList[1], dataList[2], dataList[3], dataList[4], dataList[5], cfgReader.j2meDir_)
        detailsWin.show()

##################################################################################
#
# Utilities functions
#
##################################################################################

def getHttp(hname,fname,tmp):
    global debug

    try:
        conn = httplib.HTTPConnection(hname,80,timeout=10)
    # old pythons (<2.6)
    except TypeError:
        conn = httplib.HTTPConnection(hname)

    try:
        conn.request("GET", fname)
    except Exception: 
        sendMainWindow(20016, _tr('Can not establish the connection !'))
        return False
        
    r = conn.getresponse()

    if debug: print(fname, r.status, r.reason,r.getheader('content-length'))
    
    ret = True
    
    if r.status == httplib.OK:
        if debug: print('download',fname)
        
        try:
            data = r.read()
        except Exception:
            sendMainWindow(20016, _tr('Download failed !'))
            return False
        
        f=open(os.environ.get("HOME")+os.sep+'.anyRemote'+fname+tmp, 'w')
        if f:
            f.write(data)
            f.close()
    else:
        if tmp == "":
            sendMainWindow(20016, _tr('Can not download ')+hname+fname)
            ret = False

    conn.close()

    return ret
    
def uploadJ2MEWebGui(fname, strip=False):
    global debug
    
    if debug: print('uploadJ2MEWebGui',fname)
    
    writePBar(_tr('Downloading')+' '+fname)
    
    if not getHttp("anyremote.sourceforge.net",os.sep+fname,""):
        stopPBar()
        if debug: print('uploadJ2MEWebGui: fails to upload',fname)
        return False
    
    if strip:
        f1 = os.environ.get("HOME")+os.sep+'.anyRemote'+os.sep+fname
        os.system('rm -f '+f1+'.tmp; cat '+f1+'|sed \'s/MIDlet-Jar-URL: http:\/\/anyremote.sourceforge.net\//MIDlet-Jar-URL: /\' > '+f1+'.tmp;mv '+f1+'.tmp '+f1);
    
    return True

def getJ2MEClientVersion(fname):
    global debug
    if debug: print('Check version of J2ME client from file',fname)
    return getResult('cat '+fname+'|grep MIDlet-Version|tr -d \" \"|cut -f 2 -d \":\"','main')

def checkJ2MEClientUpdate():
    global debug
    
    if not getHttp("anyremote.sourceforge.net",os.sep+"anyRemote-16.jad",".check"):
        return "-1"
        
    newver = getJ2MEClientVersion(os.environ.get("HOME")+os.sep+'.anyRemote'+os.sep+'anyRemote-16.jad.check')
    oldver = getJ2MEClientVersion(getJ2MEPath()+os.sep+'anyRemote-16.jad')
    
    if debug: print('compare new=',newver,' and old=',oldver)
    
    nvers = newver.split('.')
    overs = oldver.split('.')
    
    l = len(nvers)
    if len(overs) > l: 
        l = len(overs)
    
    foundnew = False
    for i in range(l):
        
        if len(overs) < i:
            oi = 0
        else:
            oi = overs[i]
            
        if len(nvers) < i:
            ni = 0
        else:
            ni = nvers[i]
        
        #if debug: print('(',i,') compare new=',ni,' and old=',oi)
        if ni > oi:
            foundnew = True
            break

    if (foundnew):
        return  newver
        
    return ""

#
#
#
def getMode(cfile):
    global appData
    for k, v in appData.items():
        if appData[k][5] == cfile:
            return appData[k][7]
    return ''

def killAnyRemote():
    global debug
    if debug: print('killall -2 anyremote')
    os.system('killall -2 anyremote > /dev/null')
    
def startAnyRemote(cfile):
    global debug, cfgReader, port
    
    if cfile == '':
      errorMessage(_tr('Can not start anyRemote. No configuation file specified!'))
      return	

    killAnyRemote()
    
    tool = isInstalled('anyremote')
    if tool == 'NOK':
      return

    time.sleep(0.5)
    
    useType = getMode(cfile)
    
    mode = 'Server'
    if cfgReader.showAt_:
        mode = 'AT'
    if cfgReader.showBm_:
        gui = 'Bemused'
    if cfgReader.showIView_:
        mode = 'iViewer'

    log = ''
    if debug:
        log = ' -log'
   
    dev = ''
    rconn = ''
    
    if mode == 'Server':
        connStr = cfgReader.getConnectString()
        if connStr != '':
                dev = ' -s '+connStr
  
    elif mode == 'AT':
        if cfgReader.deviceAT_ != '':
                dev = ' -s '+cfgReader.deviceAT_
    
        if cfgReader.autoReconn_ != '':
            rcomm = ' -a '
        
    elif mode == 'Bemused':
        if cfgReader.deviceBm_ != '':
                dev = ' -s '+cfgReader.deviceBm_
    
    elif mode == 'iViewer':
        
        if cfgReader.deviceIV_ != '':
                dev = ' -s '+cfgReader.deviceIV_
    
    else:
         if cfgReader.autoReconn_ != '':
            rconn = ' -a '

                
    to_path = os.environ.get("HOME") + os.sep + '.anyRemote' + os.sep

    cmd = 'anyremote -fe ' + port + log + ' -f ' + str(cfile) + dev + rconn + ' > '+ to_path + 'anyremote.stdout &'
    if debug: print(cmd)
    os.system(cmd)
 
#####################################################################################    

def sendEvent(receiver, eid, data):
    global quitFlag, app
    
    if quitFlag: return
    
    event = QEventEx(eid,data)
    app.postEvent(receiver, event)
    
def loadFromTheme(theme,icon):
    dir = '/usr/share/icons/'+theme+'/16x16'
    if not os.path.exists(dir):
    	return QPixmap()
    	
    iFile = getResult('find '+dir+' -name '+icon+'.[ps][nv]g|head -1', 'main')
    
    if iFile != '':
    	pixmap = QPixmap(iFile)
    	if not pixmap.isNull():
    	    return pixmap

    return QPixmap()

def loadPixmap(icon):
    try:
        curTheme = str(QIcon.themeName())

        if QIcon.hasThemeIcon(icon):
            ic = QIcon.fromTheme(icon)
            return ic.pixmap(16,16)

        themes = ["oxygen","crystalsvg","Humanity","Tango","gnome","hicolor"]
        for theme in themes:
            if theme == curTheme:
               continue
            px = loadFromTheme(theme,icon)
            if not px.isNull():
                return px
    # fix QT versioning issues
    except AttributeError:
        pass
    
    # hardcode it, last resort
    pixmap = QPixmap("/usr/share/pixmaps/"+icon+".svg")

    if pixmap.isNull():
        pixmap = QPixmap("/usr/share/pixmaps/"+icon+".png")
    
    # use from current directory, the latest resort
    if debug and pixmap.isNull():
        pixmap = QPixmap("data/"+icon+".svg")
    
    if debug and pixmap.isNull():
        pixmap = QPixmap("data/"+icon+".png")
        
    if debug and pixmap.isNull(): 
        print("loadPixmap NO PIXMAP",icon)
    
    return pixmap 

def getPixmap(icon):
    if icon.endswith('.png') or icon.endswith('.svg'):
        icon = icon[0:-4]
    
    pixmap = QPixmapCache.find(icon)
    
    if pixmap == None:
        pixmap = loadPixmap(icon)
        QPixmapCache.insert(icon,pixmap)
    return pixmap

def getIcon(icon):

    if icon.endswith('.png') or icon.endswith('.svg'):
        icon = icon[0:-4]
    try:
        if QIcon.hasThemeIcon(icon):
            ic = QIcon.fromTheme(icon)
            return ic
    # fix QT versioning issues
    except AttributeError:
        pass

    return QIcon(getPixmap(icon))

# Load icon for cfg. file.
def loadAppIcon(name1):
    ic = getIcon(name1)
    if ic.isNull():
        ic = QIcon(getPixmap("document-open"))
    return ic

# Load icon for menu/button/etc.
def loadIcon(name1,name2):

    ic = getIcon(name1)
    
    if ic.isNull():
        ic = getIcon(name2)
    
    return ic

def setTrayIcon(iconName):
    global systray
    if systray:
        systray.setIcon(QIcon(getPixmap(iconName)))

def resetIcon():
    setTrayIcon("kanyremote_small")

def yesno(widget, message):
    ret = QMessageBox.question(widget,"kAnyRemote",message,QMessageBox.Yes|QMessageBox.Default,QMessageBox.No|QMessageBox.Escape)
    return (ret == QMessageBox.Yes)

def errorBox(widget, msg):
    ret = QMessageBox.warning(widget,"kAnyRemote",msg,QMessageBox.Ok);

def infoBox(widget, msg):
    ret = QMessageBox.warning(widget,"kAnyRemote",msg,QMessageBox.Ok);

def infoMessage(msg):
    sendMainWindow(20015, msg)

def codeMessage(code):
    sendMainWindow(code, '')

def codeMessage2(code,val):
    sendMainWindow(code, val)

def statusMessage(val):
    sendMainWindow(val,'')

def getJ2MEPath():
    path = ''
    if os.path.exists(os.environ.get("HOME") + os.sep + '.anyRemote' + os.sep + 'anyRemote-16.jad'):
        path = os.environ.get("HOME") + os.sep + '.anyRemote'
    return path

def isInstalled(app):
    dirs = os.getenv('PATH').split(':')
    for d in dirs:
        if os.path.exists(d+'/'+app):
            return 'OK'
    return 'NOK'

def reqVersion(cmd):
    res = getResult(cmd,'main')
    if res == 'OK':
         return 'OK'
    return 'NOK'
        
def getResult(cmd, suffix):
    #print('getResult',cmd, suffix)

    toFile = os.environ.get("HOME") + os.sep + '.anyRemote' + os.sep + 'kanyremote-' + suffix + '.tmp'
    os.system(cmd + '> ' + toFile)
    line = getLineTmpFile(toFile)
    return line.replace('\n','')

def getLineTmpFile(toFile):
    fd = open(toFile,'r')
    ln = ''
    if fd:
            ln=fd.readline()
            fd.close()
    return ln
   
def clearAppsList():   
    global appData
    appData.clear()

def addAppToList(aName, aIcon, app, aRun, isRun, cfgFile, status, aMode, aType, aDesc):
    global appData
     
    if aIcon == '':
        aIcon = 'document-open.png'
    
    idx = len(appData)
    
    for oneMode in aMode: 
        appData[idx] = [aName, aIcon, app, aRun, isRun, cfgFile, status, _tr(oneMode), _tr(aType), aDesc]
        idx = idx + 1

#####################################################################################    

def addDevice(v1, v2, v3, v4, v5):
    global bt_devices
    bt_devices[v1] = [v1,v2,v3,v4,v5,'']

def saveDevices():

    global bt_devices

    # anyRemote related part
    cfg = os.environ.get("HOME")+os.sep+'.anyRemote'
    if os.path.exists(cfg):
            pass
    else:
            os.mkdir(cfg)

    f=open(cfg + os.sep + AR_FILE_DEVICES, 'w')
    if f:
            for k, v in bt_devices.items():

                row = bt_devices[k]
                v0 = row[0]
                v1 = row[1]
                v2 = row[2]
                v3 = row[3]
                v4 = row[4]
                
                f.write('Device='+v0+','+v1+','+v2+','+v3+','+v4+'\n')

            f.close()

#####################################################################################    

def btVerify():
    bts = _tr('Active')
    hcid = getResult('echo \'A=`ps -ef|grep hcid|grep -v grep|grep -v defunct`; if [ "x$A" == "x" ]; then echo NOK; else echo OK; fi\' | bash -f -s','main')
    if debug: print('btVerify1',hcid)
    
    if hcid == 'NOK':
        hcid = getResult('echo \'A=`ps -ef|grep bluetoothd|grep -v grep|grep -v defunct`; if [ "x$A" == "x" ]; then echo NOK; else echo OK; fi\' | bash -f -s','main')
        if debug: print('btVerify2',hcid)
        if hcid == 'OK':
            hcid = getResult('echo \'A=`ps -ef|grep bluetoothd|grep "\-C"|grep -v grep|grep -v defunct`; if [ "x$A" == "x" ]; then echo NOK; else echo OK; fi\' | bash -f -s','main')
            if hcid == 'NOK':
                bts = _tr('Bluetooth connection will not work. It needs to run bluetoothd daemon with -C option')
        else:
            bts = _tr('Not active')
     
    return hcid,bts

def initVerify():
    tool = isInstalled('anyremote')
    if tool == 'NOK':
        sendMainWindow(20016, _tr("anyRemote not found !\nPlease install it or correct $PATH"))

    tool = isInstalled('sdptool')
    if tool == 'NOK':
         sendMainWindow(20016, _tr("sdptool not found !\nPlease install bluez-util"))

def showDetailsWin(dataList):
    sendMainWindow(20020, dataList)
    
def closeDetailsWin():
    global detailsWin
    try:
        detailsWin.destroy()
        detailsWin = None
    except (AttributeError, NameError):
        pass

def closeChooserWin():
    global chooserWin
    try:
        chooserWin.destroy()
        chooserWin = None
    except (AttributeError, NameError):
        pass

def showBrowserWin():
    global browserWin
    browserWin = DeviceBrowser()
    browserWin.show()

def closeBrowserWin():
    global browserWin
    try:
        browserWin.destroy()
        browserWin = None
    except (AttributeError, NameError):
        pass

def showChkCfgWin():
    global chkCfgWin
    chkCfgWin = CfgChecker()
    chkCfgWin.show()

def closeChkCfgWin():
    global chkCfgWin
    try:
        chkCfgWin.destroy()
        chkCfgWin = None
    except (AttributeError, NameError):
        pass

def getAvailableSet():
    global bt_devices

    s = []
    for k, v in bt_devices.items():
        if v[5] == AR_AVAIL:
            s.append(v[0])
    return s

def getDevSet():
    global bt_devices

    s = []
    for k, v in bt_devices.items():
        s.append(v[0])
    return s
    
def browseDevices(force):
    global cmd_array, debug
    
    if ['scan',force] in cmd_array:
        if debug: print('Skip device scan')
    else:
        queueBT('scan',force)

    return True

def restartBtComm():
    stopBtComm()
    startBtComm()
        
def timerBrowseDevices():
    global cfgReader, quitFlag, browseTimer

    if cfgReader.browseTmout_ > 0 and not quitFlag:
        browseDevices('F')
        browseTimer = QTimer.singleShot(1000*cfgReader.browseTmout_, timerBrowseDevices)

def startBtComm():
    global bt, usepybluez, cfgReader, browseTimer, browseFlag, debug
    if debug: print('startBtComm')

    if usepybluez == True and bt == None: 
        bt = BtComm(cfgReader.j2meDir_)
        bt.start()
        
        if cfgReader.browseTmout_ > 0:
            # give 5 sec. to thread for start
                browseTimer = QTimer.singleShot(5000, timerBrowseDevices)

def stopBtComm():
    global bt, browseTimer, debug
    if debug: print('stopBtComm')
    
    try:
        bt.stop()
    except (AttributeError, NameError):
        if debug: print('Exception: bt.stop()')
        pass
    time.sleep(0.5)
    bt = None

def queueBT(tag,address):
    global debug

    if debug: print('queueBT',cmd_array)
    i = len(cmd_array)-1
    if i > 0 and cmd_array[i] == ['scan','']:
        cmd_array.insert(i,[tag,address])
    else:
        cmd_array.append([tag,address]) 

def startFrontend():
    if debug: print('startFrontend',cmd_array)
    global frontEnd
    frontEnd = FrontEnd()
    frontEnd.start()

def stopFrontend():
    global debug, frontEnd
    if debug: print('stopFrontend')

    # To avoid socket.error: (98, 'Address already in use') 
    # it needs to close client socket first
    killAnyRemote()

    time.sleep(0.5)

    try:
        frontEnd.stop()
    except (AttributeError, NameError):
        pass
    
    time.sleep(0.5)   
    frontEnd = None

def usage():
    print('kanyremote [-h|--help] [-p|--port <port>] [-d|--debug] [-t|--tray] [-y|--ntray] [-e] [-n|--npybluez]')

##################################################################################
#
# Main function
#
##################################################################################

def main():

    global app, systray, debug, port, statusUpdater, cmdToSend, guiMode, usepybluez, usetray, cfgReader, mainWindow
        
    try:
        opts, args = getopt.getopt(sys.argv[1:], "dnyehotp:", ["debug", "expert", "help", "open", "tray", "ntray", "npybluez", "port="])
    except getopt.GetoptError:
        usage()
        sys.exit(2)

    cmdToSend     = ''
    statusUpdater = ''
    guiMode       = 'simple'
    usepybluez    = True
    usetray       = True
    startintray   = False
    systray       = None
     
    app = QApplication(sys.argv)
 	
    for o, a in opts:
        if o in ("-d", "--debug"):
            debug   = True
            guiMode = 'expert'
        if o in ("-e", "--expert"):
            guiMode = 'expert'
        if o in ("-h", "--help"):
            usage()
            sys.exit()
        if o in ("-p", "--port"):
            port = a
        if o in ("-t", "--tray"):
            startintray = True
        if o in ("-y", "--ntray"):
            usetray = False
        if o in ("-n", "--npybluez"):
            usepybluez = False

    if pybluez == False and usepybluez == True:
        print(_tr('Install PyBluez first !\nOr run with --npybluez option'))
        return
       
    if debug: print('Use port ' + port)
   
    cfg = os.environ.get("HOME")+os.sep+'.anyRemote'
    if not os.path.exists(cfg):
        os.mkdir(cfg)
    
    cfgSet = False
    
    cfgReader = ConfigReader()
    cfgReader.setDefaultConfig()
    if os.path.exists(cfg+os.sep+'anyremote-fe.conf'):
        cfgReader.readConfig()
        cfgSet = True
    else: 
        if os.path.exists('/usr/share/anyremote/cfg-data'):
            cfgSet = True
            cfgReader.cfgDirs.append('/usr/share/anyremote/cfg-data')
        elif os.path.exists('/usr/local/share/anyremote/cfg-data'):
            cfgSet = True
            cfgReader.cfgDirs.append('/usr/local/share/anyremote/cfg-data/')
        # quick fix for ubuntu
        elif os.path.exists('/etc/anyremote/cfg-data'):
            cfgSet = 1
            cfgReader.cfgDirs.append('/etc/anyremote/cfg-data')
            
        cfgReader.j2meDir_ = getJ2MEPath()
        
        if cfgSet == 1:
           cfgReader.setAutoStartFile()
    
    # add to cache default fallback icon
    getPixmap("document-open")
    
    app.setWindowIcon(QIcon(getPixmap("kanyremote_small")))
        
    mainWindow = kAnyRemote(cfgSet)
    
    if cfgReader.checkJ2MEUpdate_:
        if debug: print('Check J2ME client updates at web')
        jv = JCVerifier(False)
        jv.start()
    
    if startintray:
        mainWindow.isShown = 0
    else:
        mainWindow.isShown = 1
        mainWindow.show()
    
    initVerify()

    systray = None
    
    if usetray:
        usetray = QSystemTrayIcon.isSystemTrayAvailable()
    
    if usetray:

        systray = QSystemTrayIcon(mainWindow)
	
        setTrayIcon("kanyremote_off")
         
        mainWindow.trayAboutAction   = QAction(loadIcon("help-about",      "help"),_tr("About"), mainWindow)
        mainWindow.trayStartAction   = QAction(loadIcon("system-run",      "run"), _tr("Start"), mainWindow)
        mainWindow.trayStopAction    = QAction(loadIcon("dialog-close",    "stop"),_tr("Stop"),  mainWindow)
        mainWindow.trayMinMaxAction  = QAction(_tr("Minimize")+'/'+_tr("Restore"),  mainWindow)
        mainWindow.trayQuitAction    = QAction(loadIcon("application-exit","exit"),_tr("Quit"),  mainWindow)
        
        mainWindow.trayAboutAction.triggered.connect(mainWindow.helpAbout)
        mainWindow.trayStartAction.triggered.connect(mainWindow.runAction)
        mainWindow.trayStopAction.triggered.connect(mainWindow.stopAction)
        mainWindow.trayMinMaxAction.triggered.connect(mainWindow.minMaxAction)
        mainWindow.trayQuitAction.triggered.connect(mainWindow.fileExit)

        trayMenu = QMenu()
        trayMenu.addAction(mainWindow.trayAboutAction)
        trayMenu.addAction(mainWindow.trayStartAction)
        trayMenu.addAction(mainWindow.trayStopAction)
        trayMenu.addAction(mainWindow.trayMinMaxAction)
        trayMenu.addAction(mainWindow.trayQuitAction)
	
        systray.setContextMenu(trayMenu)
        #app.connect(systray, SIGNAL("quitSelected()"), mainWindow.fileExit)
        
        systray.show()

    app.lastWindowClosed.connect(app.quit)

    mainWindow.setStatusStopped()

    startFrontend()
    
    mainWindow.autoStartBackend()
    
    app.exec_()

if __name__ == "__main__":
    global debug, port, cmdToSend, appData, bt_devices, quitFlag, jup, cmd_array, mainWindow, browserWin, chooserWin, chkCfgWin, detailsWin, bt, pbarTimer, pbar, cfgFileReaderFlag

    port       = '5050'
    debug      = False
    appData    = dict()
    bt_devices = dict()
    cmdToSend  = ''
    quitFlag   = False
    cmd_array  = [] 
    mainWindow = None 
    detailsWin = None    
    browserWin = None    
    chooserWin = None    
    chkCfgWin  = None    
    bt         = None
    pbarTimer  = None
    pbar       = None
    jup        = None
    cfgFileReaderFlag = False
  
    main()
