0.1.7 release
[ussd-widget] / ussd-widget / src / usr / lib / hildon-desktop / ussd-widget.py
index ec501bb..3fbe088 100755 (executable)
@@ -5,362 +5,1088 @@ import gtk
 import hildon
 import hildondesktop
 import os
-from subprocess import *
 import cairo
+import pango
 import time
-from threading import *
 import re
+import gettext
+import fcntl
+import dbus
+import subprocess
+import gsmdecode
+import sys
+from dbus.mainloop.glib import DBusGMainLoop
+
+try :
+       t = gettext.translation('ussd-widget', '/usr/share/locale')
+       _ = t.ugettext
+except IOError:
+       print "Translation file for your language not found"
+       def retme(arg):
+               return arg
+       _ = retme
+
+ussd_languages = ["German", "English", "Italian", "French", "Spanish", "Dutch", "Swedish", "Danish", "Portuguese", "Finnish", "Norwegian", "Greek", "Turkish", "Reserved1", "Reserved2", "Unspecified"]
+ussd_languages_localized = [_("German"), _("English"), _("Italian"), _("French"), _("Spanish"), _("Dutch"), _("Swedish"), _("Danish"), _("Portuguese"), _("Finnish"), _("Norwegian"), _("Greek"), _("Turkish"), _("Reserved1"), _("Reserved2"), _("Unspecified")]
+
+# TODO Cutt off too long messages and show them in separate dialog
+# how TODO widget vertical minimum size policy
+# TODO interface for queryng from scripts. For example query from one widget can change color of another widget 
+
+class USSD_Controller:
+       def __init__( self, widget ) :
+               self.widget = widget
+               # number, parser, chain, interval, regexp, width, execute_at_start, retry pattern, font, name, language, show_message_box, message_box_parser, additional arguments, regexp group, use SMS listener, SMS number, SMS regexp, SMS timeout
+               self.default_config = ["", "", "", 0, "", 0, True, [], pango.FontDescription("Nokia Sans 18"), _("Click to update"), 15, False, "", "", 1, False, "", "", 60]
+               self.config = self.default_config
+               self.timeout_version = 0
+               self.retry_version = 0
+               self.retry_state = 0
+               self.sms_counter = 0
+               self.sms_reply = ""
+
+       def save_config( self ) :
+               configname = os.getenv("HOME")+"/.ussdWidget.conf"
+               # Aquire lock
+               lockf = open(configname+".lock", 'a')
+               fcntl.flock(lockf,fcntl.LOCK_EX)
+
+               oldconfig=""
+               try:
+                       fconfig = open(configname,"r")
+                       #Read configuration of other instances
+                       my_section = False
+                       for line in fconfig :
+                               if line[0] == '%':
+                                       my_section = line[1:].strip() == self.id
+                               if not my_section:
+                                       oldconfig += line
+                       fconfig.close()
+               except:
+                       print _("Couldn't read previous config")
+
+               fconfig = open(configname,"w")
+               fconfig.seek(0)
+               fconfig.write(oldconfig)
+               fconfig.write("%"+self.id+"\n");
+               fconfig.writelines(["# USSD query to be run by widget\n", "number="+self.config[0], "\n"])
+               fconfig.writelines(["#Parser command for widget\n", "parser="+self.config[1], "\n"])
+               fconfig.writelines(["#Parser command for banner\n", "parser_box="+self.config[12], "\n"])
+               fconfig.writelines(["#Chain command\n", "chain="+self.config[2], "\n"])
+               fconfig.writelines(["#Update interval in minutes\n", "interval="+str(self.config[3]), "\n"])
+               fconfig.writelines(["#RegExp pattern\n", "regexp="+self.config[4], "\n"])
+               fconfig.writelines(["#Widget width\n", "width="+str(self.config[5]), "\n"])
+               fconfig.writelines(["#Execute query at start\n", "query_at_start="+str(self.config[6]), "\n"])
+               fconfig.writelines(["#Retry pattern\n"])
+               fconfig.write("retry=")
+               first = True
+               for i in self.config[7]:
+                       if not first:
+                               fconfig.write ("-")
+                       fconfig.write(str(i))
+                       first = False
+               fconfig.write("\n")
+               fconfig.writelines(["#Font description\n", "font="+self.config[8].to_string(), "\n"])
+               fconfig.writelines(["#Font color\n", "text_color="+self.widget.get_text_color().to_string(), "\n"])
+               fconfig.writelines(["#Background color\n", "bg_color="+self.widget.get_bg_color().to_string(), "\n"])
+               fconfig.writelines(["#Widget name\n", "name="+self.config[9], "\n"])
+               fconfig.writelines(["#Show banner\n", "show_box="+str(self.config[11]), "\n"])
+               fconfig.writelines(["#USSD reply language\n", "language="+str(self.config[10]), "\n"])
+               fconfig.writelines(["#Additional ussdquery.py arguments\n", "args="+self.config[13], "\n"])
+               fconfig.writelines(["#Regexp matching group\n", "reggroup="+str(self.config[14]), "\n"])
+               fconfig.writelines(["#Use SMS listener\n", "listen_sms="+str(self.config[15]), "\n"])
+               fconfig.writelines(["#Number,from which SMS should come\n", "sms_number="+self.config[16], "\n"])
+               fconfig.writelines(["#SMS RegExp pattern\n", "sms_regexp="+self.config[17], "\n"])
+               fconfig.writelines(["#SMS timeout\n", "sms_timeout="+str(self.config[18]), "\n"])
+               fconfig.close()
+
+               fcntl.flock(lockf,fcntl.LOCK_UN)
+               lockf.close()
+
+       def get_config(self):
+               return self.config
+
+       def read_config( self, id ):
+               try :
+                       self.id = id
+                       config = open(os.getenv("HOME")+"/.ussdWidget.conf","r")
+
+                       error = False
+                       i = 0
+                       my_section = False
+                       for line in config :
+                               i += 1 
+                               if line[0] == '#':
+                                       continue
+                               if line[0] == '%':
+                                       my_section = line[1:].strip() == id
+                                       continue
+
+                               if not my_section:
+                                       # This is config for another instace
+                                       continue
+
+                               line=line.split('=', 1)
+                               
+                               if len(line) != 2 :
+                                       error = True
+                                       print _("Error reading config on line %(line)d. = or # expected.")%{"line":i}
+                                       continue
+                               if line[0] == "number" :
+                                       self.config[0] = line[1].strip()
+                               elif line[0] == "parser" :
+                                       self.config[1] = line[1].strip()
+                               elif line[0] == "parser_box" :
+                                       self.config[12] = line[1].strip()
+                               elif line[0] == "chain" :
+                                       self.config[2] = line[1].strip()
+                               elif line[0] == "interval" :
+                                       try:
+                                               self.config[3] = int(line[1].strip())
+                                       except:
+                                               error = True
+                                               print _("Error reading config on line %(line)d. Integer expected.")%{"line":i}
+                                               continue
+                               elif line[0] == "regexp" :
+                                       self.config[4] = line[1].strip()
+                               elif line[0] == "width" :
+                                       try:
+                                               self.config[5] = int(line[1].strip())
+                                       except:
+                                               error = True
+                                               print _("Error reading config on line %(line)d. Integer expected.")%{"line":i}
+                                               continue
+                               elif line[0] == "query_at_start" :
+                                       if line[1].strip() == "True" :
+                                               self.config[6] = True
+                                       else :
+                                               self.config[6] = False
+                               elif line[0] == "retry" :
+                                       line[1] = line[1].strip()
+                                       if line[1] != "":
+                                               line[1] = line[1].split("-")
+                                               i = 0
+                                               while i < len(line[1]) :
+                                                       try:
+                                                               line[1][i] = int(line[1][i])
+                                                       except:
+                                                               error = True
+                                                               print _("Error reading config on line %(line)d. Integer expected.")%{"line":i}
+                                                       i += 1
+                                               self.config[7] = line[1]
+                                       else:
+                                               self.config[7] = []
+                                               continue
+                               elif line[0] == "font" :
+                                       try:
+                                               self.config[8] = pango.FontDescription(line[1].strip())
+                                       except:
+                                               error = True
+                                               print _("Error reading config on line %(line)d. Pango font description expected.")%{"line":i}
+                                               continue
+                               elif line[0] == "bg_color" :
+                                       try:
+                                               self.widget.set_bg_color(gtk.gdk.color_parse(line[1].strip()))
+                                       except:
+                                               error = True
+                                               print _("Error reading config on line %(line)d. Expected color definition.")%{"line":i}
+                               elif line[0] == "text_color" :
+                                       try:
+                                               self.widget.set_text_color(gtk.gdk.color_parse(line[1].strip()))
+                                       except:
+                                               error = True
+                                               print _("Error reading config on line %(line)d. Expected color definition.")%{"line":i}
+                               elif line[0] == "name" :
+                                       self.config[9] = line[1].strip()
+                               elif line[0] == "show_box" :
+                                       if line[1].strip() == "True" :
+                                               self.config[11] = True
+                                       else :
+                                               self.config[11] = False
+                               elif line[0] == "language" :
+                                       try:
+                                               if int(line[1].strip()) >=0 and int(line[1].strip()) < len(ussd_languages):
+                                                       self.config[10] = int(line[1].strip())
+                                               else:
+                                                       error = True
+                                                       print _("Error reading config on line %(line)d. Unknown language code.")%{"line":i}
+                                       except:
+                                               error = True
+                                               print _("Error reading config on line %(line)d. Integer expected.")%{"line":i}
+                               elif line[0] == "args" :
+                                       self.config[13] = line[1].strip()
+                               elif line[0] == "reggroup" :
+                                       try:
+                                               self.config[14] = int(line[1].strip())
+                                       except:
+                                               error = True
+                                               print _("Error reading config on line %(line)d. Integer expected.")%{"line":i}
+                                               continue
+                               elif line[0] == "listen_sms" :
+                                       if line[1].strip() == "True" :
+                                               self.config[15] = True
+                                       else :
+                                               self.config[15] = False
+                               elif line[0] == "sms_number" :
+                                       self.config[16] = line[1].strip()
+                               elif line[0] == "sms_regexp" :
+                                       self.config[17] = line[1].strip()
+                               elif line[0] == "sms_timeout" :
+                                       try:
+                                               self.config[18] = int(line[1].strip())
+                                       except:
+                                               error = True
+                                               print _("Error reading config on line %(line)d. Integer expected.")%{"line":i}
+                                               continue
+                               else :
+                                       error = True
+                                       print _("Error reading config on line %(line)d. Unexpected variable: ")%{"line":i}+line[0]
+                                       continue 
+
+                       config.close()
+
+                       if error :
+                               self.widget.error = 1
+                               self.widget.set_text (_("Config error"), 5000)  
+
+                       return self.config
+               except  IOError:
+                       self.widget.error = 1
+                       self.widget.set_text (_("Config error"), 0)
+                       print _("IO error while reading config")
+
+                       return self.default_config
+
+       def on_show_settings( self, widget ) :
+               dialog = UssdConfigDialog(self.config, self.widget.get_bg_color(), self.widget.get_text_color())
+
+               while True:
+                       if dialog.run() != gtk.RESPONSE_OK :
+                               dialog.destroy()
+                               return
+
+                       test = check_regexp(dialog.regexp.get_text()) 
+                       if test :
+                               dialog.on_error_regexp(test)
+                               continue
+
+                       # Check, that we have ussd number
+                       if not check_number(dialog.ussdNumber.get_text()):
+                               dialog.on_error_ussd_number()
+                               continue
+               
+                       if not check_number(dialog.sms_number.get_text()):
+                               dialog.on_error_sms_number()
+                               continue
+
+                       # Parse retry pattern
+                       retry = dialog.retryEdit.get_text().strip()
+                       if retry != "" :
+                               retry = retry.split("-")
+                               i = 0
+                               while i < len(retry) :
+                                       try:
+                                               retry[i] = int(retry[i])
+                                       except:
+                                               dialog.on_error_retry_pattern()
+                                               break 
+                                       i += 1
+                       
+                               if i < len(retry):
+                                       continue
+                       else :
+                               retry = []
+
+                       break
+
+               self.config = [
+                       dialog.ussdNumber.get_text(), 
+                       dialog.parser.get_text(), 
+                       dialog.chain.get_text(), 
+                       dialog.update_interval.get_value(), 
+                       dialog.regexp.get_text(),
+                       dialog.widthEdit.get_value(),
+                       dialog.query_at_start.get_active(),
+                       retry,
+                       dialog.font,
+                       dialog.wname.get_text(),
+                       dialog.language.get_active(),
+                       dialog.show_box.get_active(),
+                       dialog.b_parser.get_text(),
+                       dialog.args.get_text(),
+                       dialog.reggroup.get_value(),
+                       dialog.sms_listener.get_active(),
+                       dialog.sms_number.get_text(),
+                       dialog.sms_regexp.get_text(),
+                       dialog.sms_timeout.get_value() 
+               ]
+
+               widget.set_bg_color(dialog.bg_color)
+               widget.set_text_color(dialog.text_color)
+
+               self.save_config()
+
+               widget.set_width(self.config[5])        
+               self.reset_timed_renew()
+               self.widget.label.modify_font(self.config[8])
+
+               dialog.destroy()
+
+               # Before running this function widget wasn't configured
+               if self.config == self.default_config:
+                       self.widget.set_text(_("Click to update"))
+               return
+
+       def handle_sms(self, pdumsg, msgcenter, message, sendernumber):
+               # Timeout was recieved first
+               if self.sms_ready:
+                       return
+
+               if self.config[16] == "" or self.config[16] == sendernumber:
+                       pdu = gsmdecode.decode_pdu (pdumsg)
+                       if pdu != None :
+                               self.sms_reply += pdu['user_data']
+                               if not pdu['part']:
+                                       if self.config[17] == "" or re.search( self.config[17], message, re.MULTILINE | re.UNICODE ):
+                                               self.sms_ready = True
+                                               self.sms_signal.remove()
+                                               self.process_reply()
+
+       def callback_ussd_data( self, source, condition ):
+               if condition == gobject.IO_IN or condition == gobject.IO_PRI :
+                       data = source.read( )
+                       if len( data ) > 0 :
+                               self.cb_reply += data
+                               return True
+                       else :
+                               self.cb_ready = 1
+                               return False
+
+               elif condition == gobject.IO_HUP or condition == gobject.IO_ERR :
+                       self.cb_ready = 1
+                       self.ussd_ready = True
+                       self.process_reply()
+                       return False
+
+               print (_("serious problems in program logic"))
+               # This will force widget to show error message
+               self.cb_reply = ""
+               self.cb_ready = 1
+               self.process_reply()
+               return False
+
+
+       def call_external_script( self, ussd_code, language ):
+               self.cb_ready = 0
+               self.cb_reply = "";
+               p = subprocess.Popen(['/usr/bin/ussdquery.py', ussd_code, "-l", ussd_languages[language]] + smart_split_string(self.config[13],"%","&"), stdout=subprocess.PIPE)
+               gobject.io_add_watch( p.stdout, gobject.IO_IN | gobject.IO_PRI | gobject.IO_HUP | gobject.IO_ERR , self.callback_ussd_data )
+
+       def ussd_renew(self, widget, event):
+               if self.widget.processing == 0:
+                       if self.config :
+                               widget.processing = 1
+                               widget.set_text(_("Processing"), 0)
+
+                               self.ussd_ready = False
+                               self.sms_ready = False
+                               self.sms_reply = ""
+
+                               if self.config[15]:
+                                       self.sms_counter += 1
+                                       self.retry_timer = gobject.timeout_add (1000*self.config[18], self.sms_timeout, self.sms_counter)
+                                       
+                                       DBusGMainLoop(set_as_default=True)
+                                       self.bus = dbus.SystemBus()
+                                       self.sms_signal = self.bus.add_signal_receiver(self.handle_sms, path='/com/nokia/phone/SMS',   dbus_interface='Phone.SMS', signal_name='IncomingSegment')
+
+                               self.call_external_script( self.config[0], self.config[10] )
+                       else :
+                               widget.processing = 0
+                               widget.error = 1
+                               widget.set_text(_("No config"), 0)
+
+       def process_reply( self ):
+               if not self.ussd_ready or not self.sms_ready and self.config[15]:
+                       return
+
+               reply = self.cb_reply.strip()
+               sms_reply = self.sms_reply.strip()
+               
+               if reply == "" or self.config[15] and sms_reply == "" :
+                       self.widget.error = 1
+                       self.widget.set_text (_("Error"), 5000)
+                       if self.retry_state == len(self.config[7]):
+                               self.retry_version += 1
+                               self.retry_state = 0
+                       else :
+                               self.retry_timer = gobject.timeout_add (1000*self.config[7][self.retry_state], self.retry_renew, self.retry_version)
+                               self.retry_state += 1
+               else :
+                       self.widget.error = 0
+                       # Apply regexp
+                       reresult1 = reresult2 = None
+                       if self.config[4] != "":
+                               reresult1 = re.search( self.config[4], reply, re.MULTILINE | re.UNICODE )
+                       if self.config[17] != "":
+                               reresult2 = re.search( self.config[17], sms_reply, re.MULTILINE | re.UNICODE )
+                       w_reply = b_reply = reply
+                       if self.widget.error == 0:
+                               # Pass to box parser
+                               if self.config[12] != "" and self.config[11]:
+                                       try:
+                                               p = subprocess.Popen(smart_split_string(self.config[12], reply, sms_reply, reresult1, reresult2), stdout=subprocess.PIPE)
+                                               b_reply = p.communicate()[0].strip()
+                                       except Exception, e:
+                                               print _("Couldn't exec banner parser:")+str(e)
+                                               self.widget.error = 1
+                               else:
+                                       if self.config[4] != "":
+                                               try :
+                                                       b_reply = reresult1.group( self.config[14] )
+                                               except Exception, e:
+                                                       self.widget.error = 1
+                                                       b_reply = _("Group not found: \n") + reply
+                                        
+                               # Pass to widget parser
+                               if self.config[1] != "":
+                                       try:
+                                               p = subprocess.Popen(smart_split_string(self.config[1], reply, sms_reply, reresult1, reresult2), stdout=subprocess.PIPE)
+                                               w_reply = p.communicate()[0].strip()
+                                       except Exception, e:
+                                               print _("Couldn't exec widget parser:")+str(e)
+                                               self.widget.error = 1
+                               else:
+                                       if self.config[4] != "":
+                                               try :
+                                                       w_reply = reresult1.group( self.config[14] )
+                                               except Exception, e:
+                                                       self.widget.error = 1
+                                                       w_reply = _("Group not found: \n") + reply
+                               # Pass to chain
+                               if self.config[2] != "":
+                                       try:
+                                               p = subprocess.Popen(smart_split_string(self.config[2], reply, sms_reply, reresult1, reresult2))
+                                       except Exception, e:
+                                               print _("Couldn't exec chain:")+str(e)
+                                               self.widget.error = 1
+                       if self.config[11]:
+                               banner = hildon.hildon_banner_show_information (self.widget, "", b_reply)
+                               banner.set_timeout (5000)
+                               b_reply
+                       self.widget.set_text(w_reply)
+               self.widget.processing = 0
+
+       def sms_timeout(self, version):
+               if version == self.sms_counter :
+                       self.sms_reply = ""
+                       self.sms_ready = True
+                       self.sms_signal.remove()
+                       self.process_reply()
+               return False
+
+       def timed_renew(self, version):
+               if version < self.timeout_version :
+                       return False
+               self.ussd_renew(self.widget, None)
+               return True
+
+       def retry_renew(self,version):
+               if self.widget.error == 0 or self.widget.processing == 1 or version < self.retry_version :
+                       return False
+               self.ussd_renew(self.widget, None)
+               return False
+
+       def reset_timed_renew (self) :
+               self.timeout_version += 1
+               if self.config[3] != 0 :
+                       self.timer = gobject.timeout_add (60000*self.config[3], self.timed_renew, self.timeout_version)
 
 class pHelpDialog(gtk.Dialog):
-    def __init__(self, heading, text):
-        gtk.Dialog.__init__(self, heading, None,
-                            gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_NO_SEPARATOR,
-                            ("OK", gtk.RESPONSE_OK))
-        self.vbox.add(gtk.Label(text))
-        self.show_all()
-        self.parent
+       def __init__(self, heading, text):
+               gtk.Dialog.__init__(self, heading, None,
+                       gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_NO_SEPARATOR,
+                       (_("OK").encode("utf-8"), gtk.RESPONSE_OK))
+               label = gtk.Label(text)
+               label.set_line_wrap (True)
+               self.vbox.add(label)
+               self.show_all()
+               self.parent
 
 class UssdConfigDialog(gtk.Dialog):
-    def __init__(self, config):
-        gtk.Dialog.__init__(self, "USSD widget", None,
-                            gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_NO_SEPARATOR,
-                            ("Save", gtk.RESPONSE_OK))
-        self.ussdNumber = hildon.Entry(gtk.HILDON_SIZE_AUTO)
-        self.ussdNumber.set_text(config[0])
-        self.parser = hildon.Entry(gtk.HILDON_SIZE_AUTO)
-        self.parser.set_text(config[1])
-        self.chain = hildon.Entry(gtk.HILDON_SIZE_AUTO)
-        self.chain.set_text(config[2])
-        self.update_interval = hildon.Entry(gtk.HILDON_SIZE_AUTO)
-        self.update_interval.set_text(config[3])
-        self.regexp = hildon.Entry(gtk.HILDON_SIZE_AUTO)
-        self.regexp.set_text(config[4])
-
-        phelp = gtk.Button("?")
-        phelp.connect("clicked", on_show_phelp)
-
-        chelp = gtk.Button("?")
-        chelp.connect("clicked", on_show_chelp)
-
-        reghelp = gtk.Button("?")
-        reghelp.connect("clicked", on_show_reghelp)
-
-        numberBox = gtk.HBox()
-        numberBox.add(gtk.Label("USSD number"))
-        numberBox.add(self.ussdNumber)
-        self.vbox.add(numberBox)
-
-        parserBox = gtk.HBox()
-        parserBox.add(gtk.Label("Parser"))
-        parserBox.add(phelp)
-        parserBox.add(self.parser)
-        self.vbox.add(parserBox)
-
-        chainBox = gtk.HBox()
-        chainBox.add(gtk.Label("Chain"))
-        chainBox.add(chelp)
-        chainBox.add(self.chain)
-        self.vbox.add(chainBox)
-
-        updateBox = gtk.HBox()
-        updateBox.add(gtk.Label("Update every "))
-        updateBox.add(self.update_interval)
-        updateBox.add(gtk.Label(" minutes (BROKEN)"))
-        self.vbox.add(updateBox)
-
-        regexpBox = gtk.HBox()
-        regexpBox.add(gtk.Label("RegExp"))
-        regexpBox.add(reghelp)
-        regexpBox.add(self.regexp)
-        self.vbox.add(regexpBox)
-
-        self.show_all()
-        self.parent
-
-def smart_split_string (str, query) :
-    word = ""
-    result = []
-    # Is simbol backslashed?
-    bs = 0
-    # Quotes: 1 - ", 2 - ', 0 - no quotes
-    qs = 0
-    for i in range(len(str)) :
-      if bs == 0 and (str[i] == '"' and qs == 1 or str[i] == "'" and qs == 2) :
-          qs = 0
-      elif bs == 0 and qs == 0 and (str[i] == '"' or str[i] == "'") :
-          if str[i] == '"':
-              qs = 1
-          else :
-              qs = 2
-      elif bs == 0 and str[i] == '\\' :
-          bs = 1
-      elif bs == 0 and str[i] == '%' :
-          word += query
-          ws = 0
-      else :
-          if bs == 1 and str[i] != '\\' and str[i] != '"' and str[i] != "'" :
-              word += "\\"
-          if qs == 0 and (str[i] == " " or str[i] == "\t") :
-              if word != "" :
-                  result.append(word)
-                  word = ""
-          else :
-              word += str[i]
-          bs = 0 
-    if word != "" :
-        result.append(word)
-    return result 
-
-def get_config():
-    try :
-        config = open(os.getenv("HOME")+"/.ussdWidget.conf","r")
-        config.readline()
-        config.readline()
-        number = config.readline().strip()
-        config.readline()
-        parser = config.readline().strip()
-        config.readline()
-        chain = config.readline().strip()
-        config.readline()
-        interval = config.readline().strip()
-        config.readline()
-        regexp = config.readline().strip()
-        config.close()
-        return [number, parser, chain, interval, regexp]
-    except  IOError:
-        return None
-
-def set_config(config):
-    fconfig = open(os.getenv("HOME")+"/.ussdWidget.conf","w")
-    fconfig.writelines(["# Parameters are taken by line number, do not move them\n", "# USSD query to be run by widget\n", config[0], "\n"])
-    fconfig.writelines(["Parser command\n", config[1], "\n"])
-    fconfig.writelines(["Chain command\n", config[2], "\n"])
-    fconfig.writelines(["Update interval in minutes\n", config[3], "\n"])
-    fconfig.writelines(["RegExp pattern\n", config[4], "\n"])
-    fconfig.close()
+       def __init__(self, config, bg_color, text_color):
+               gtk.Dialog.__init__(self, _("USSD widget"), None, 
+                       gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_NO_SEPARATOR,
+                       (_("Save").encode("utf-8"), gtk.RESPONSE_OK))
+
+               self.font = config[8]
+               self.bg_color = bg_color
+               self.text_color = text_color
+
+               self.set_size_request(-1, 400)
+               self.ussdNumber = hildon.Entry(gtk.HILDON_SIZE_AUTO)
+               self.ussdNumber.set_text(config[0])
+               self.parser = hildon.Entry(gtk.HILDON_SIZE_AUTO)
+               self.parser.set_text(config[1])
+               self.b_parser = hildon.Entry(gtk.HILDON_SIZE_AUTO)
+               self.b_parser.set_text(config[12])
+
+               self.chain = hildon.Entry(gtk.HILDON_SIZE_AUTO)
+               self.chain.set_text(config[2])
+               self.update_interval = hildon.NumberEditor(0, 9999)
+               self.update_interval.set_value(config[3])
+               self.regexp = hildon.Entry(gtk.HILDON_SIZE_AUTO)
+               self.regexp.set_text(config[4])
+               self.widthEdit = hildon.NumberEditor(0, 1000)
+               self.widthEdit.set_value(config[5])
+               self.retryEdit = hildon.Entry(gtk.HILDON_SIZE_AUTO)
+               self.args = hildon.Entry(gtk.HILDON_SIZE_AUTO)
+               self.args.set_text(config[13])
+               self.reggroup = hildon.NumberEditor(0, 255)
+               self.reggroup.set_value(config[14])
+               
+               selector = hildon.TouchSelector(text=True)
+               for i in ussd_languages_localized:
+                       selector.append_text(i)
+               self.language = hildon.PickerButton(gtk.HILDON_SIZE_AUTO, hildon.BUTTON_ARRANGEMENT_HORIZONTAL)
+               self.language.set_selector(selector)
+               self.language.set_active(config[10])
+               self.language.set_title(_("USSD reply language"))
+               self.language.set_size_request(-1, -1)
+
+               self.wname = hildon.Entry(gtk.HILDON_SIZE_AUTO)
+               self.wname.set_text(config[9])
+               self.show_box = gtk.CheckButton(_("Enable banner. Parser:"))
+               self.show_box.connect("toggled", self.show_box_changed)
+               self.show_box.set_active(config[11])
+
+               text = ""
+               for i in config[7]:
+                       if text != "":
+                               text += "-"
+                       text += str(i)
+               self.retryEdit.set_text(text)
+
+               self.query_at_start = gtk.CheckButton(_("Execute query on start"))
+               self.query_at_start.set_active(config[6])
+
+               self.fontButton = gtk.Button(_("Font"))
+               self.fontButton.connect("clicked", self.on_show_font_selection)
+
+               self.colorButton = gtk.Button(_("Background color"))
+               self.colorButton.connect("clicked", self.on_show_color_selection)
+               self.textColorButton = gtk.Button(_("Text color"))
+               self.textColorButton.connect("clicked", self.on_show_text_color_selection)
+               
+               phelp = gtk.Button("?")
+               phelp.connect("clicked", self.on_show_phelp)
+               
+               bphelp = gtk.Button("?")
+               bphelp.connect("clicked", self.on_show_bphelp)
+
+               chelp = gtk.Button("?")
+               chelp.connect("clicked", self.on_show_chelp)
+
+               reghelp = gtk.Button("?")
+               reghelp.connect("clicked", self.on_show_reghelp)
+
+               retryhelp = gtk.Button("?")
+               retryhelp.connect("clicked", self.on_show_retryhelp)
+               
+               numberhelp = gtk.Button("?")
+               numberhelp.connect("clicked", self.on_show_number_help)
+
+               area = hildon.PannableArea()
+               self.vbox.add(area)
+               vbox = gtk.VBox()
+               area.add_with_viewport(vbox)
+               
+               numberBox = gtk.HBox()
+               numberLabel = gtk.Label(_("USSD number"))
+               numberLabel.set_alignment(0,0.6)
+               numberLabel.set_size_request(100, -1)
+               numberhelp.set_size_request(1, -1)
+               self.ussdNumber.set_size_request(200, -1)
+               numberBox.add(numberLabel)
+               numberBox.add(numberhelp)
+               numberBox.add(self.ussdNumber)
+               vbox.add(numberBox)
+
+               vbox.add(self.query_at_start)
+
+               nameBox = gtk.HBox()
+               nameLabel = gtk.Label(_("Name"))
+               nameLabel.set_alignment(0,0.6)
+               nameLabel.set_size_request(100, -1)
+               self.wname.set_size_request(200, -1)
+               nameBox.add(nameLabel)
+               nameBox.add(self.wname)
+               vbox.add(nameBox)
+
+               parserBox = gtk.HBox()
+               parserLabel = gtk.Label(_("Parser for widget"))
+               parserLabel.set_alignment(0,0.6)
+               parserLabel.set_size_request(200, -1)
+               phelp.set_size_request(10, -1)
+               parserBox.add(parserLabel)
+               parserBox.add(phelp)
+               vbox.add(parserBox)
+               vbox.add(self.parser)
+               
+               b_parserBox = gtk.HBox()
+               self.show_box.set_size_request(200, -1)
+               bphelp.set_size_request(10, -1)
+               b_parserBox.add(self.show_box)
+               b_parserBox.add(bphelp)
+               vbox.add(b_parserBox)
+               vbox.add(self.b_parser)
+               
+               chainBox = gtk.HBox()
+               chainLabel = gtk.Label(_("Chain"))
+               chainLabel.set_alignment(0,0.6)
+               chainLabel.set_size_request(200, -1)
+               chelp.set_size_request(10, -1)
+               chainBox.add(chainLabel)
+               chainBox.add(chelp)
+               vbox.add(chainBox)
+               vbox.add(self.chain)
+
+               regexpBox = gtk.HBox()
+               regexpLabel = gtk.Label(_("Regular expression"))
+               regexpLabel.set_alignment(0,0.6)
+               regexpLabel.set_size_request(200, -1)
+               regexpGroupLabel = gtk.Label(_("Group"))
+               regexpGroupLabel.set_size_request(1, -1)
+               reghelp.set_size_request(10, -1)
+               regexpBox.add(regexpLabel)
+               regexpBox.add(reghelp)
+               regexpBox.add(regexpGroupLabel)
+               vbox.add(regexpBox)
+               self.reggroup.set_size_request(1,-1);
+               self.regexp.set_size_request(250,-1);
+               regexpInputBox = gtk.HBox()
+               regexpInputBox.add(self.regexp)
+               regexpInputBox.add(self.reggroup)
+               vbox.add(regexpInputBox)                
+
+               widthBox = gtk.HBox()
+               widthLabel = gtk.Label(_("Max. width"))
+               widthLabel.set_alignment(0,0.6)
+               symbolsLabel = gtk.Label(_("symbols"))
+               widthLabel.set_size_request(140, -1)
+               self.widthEdit.set_size_request(50, -1)
+               symbolsLabel.set_size_request(40,-1)
+               widthBox.add(widthLabel)
+               widthBox.add(self.widthEdit)
+               widthBox.add(symbolsLabel)
+               vbox.add(widthBox)
+
+               updateBox = gtk.HBox()
+               updateLabel = gtk.Label(_("Update every"))
+               updateLabel.set_alignment(0,0.6)
+               minutesLabel = gtk.Label(_("minutes"))
+               updateLabel.set_size_request(140, -1)
+               self.update_interval.set_size_request(50, -1)
+               minutesLabel.set_size_request(40, -1)
+               updateBox.add(updateLabel)
+               updateBox.add(self.update_interval)
+               updateBox.add(minutesLabel)
+               vbox.add(updateBox)
+
+               retryBox = gtk.HBox()
+               retryLabel = gtk.Label(_("Retry pattern"))
+               retryLabel.set_alignment(0,0.6)
+               retryLabel.set_size_request(200, -1)
+               retryhelp.set_size_request(10, -1)
+               retryBox.add(retryLabel)
+               retryBox.add(retryhelp)
+               vbox.add(retryBox)
+               vbox.add(self.retryEdit)                
+               
+               argsLabel = gtk.Label(_("Additional ussdquery.py options"))
+               argsLabel.set_alignment(0,0.6)
+               vbox.add(argsLabel)
+               vbox.add(self.args)             
+               
+               viewBox = gtk.HBox()
+               viewBox.add(self.fontButton)
+               viewBox.add(self.textColorButton)
+               viewBox.add(self.colorButton)
+               vbox.add(viewBox)
+       
+               self.sms_box = gtk.VBox()       
+               self.sms_listener = gtk.CheckButton(_("Enable SMS listener."))
+               self.sms_listener.connect("toggled", self.sms_box_changed)
+               self.sms_listener.set_active(config[15])
+               vbox.add (self.sms_listener)
+
+               self.sms_number = hildon.Entry(gtk.HILDON_SIZE_AUTO)
+               self.sms_number.set_text(config[16])
+               smsNumberBox = gtk.HBox()
+               smsNumberLabel = gtk.Label(_("SMS number"))
+               smsNumberLabel.set_alignment(0,0.6)
+               smsNumberLabel.set_size_request(100, -1)
+               self.sms_number.set_size_request(200, -1)
+               smsNumberBox.add(smsNumberLabel)
+               smsNumberBox.add(self.sms_number)
+               self.sms_box.add(smsNumberBox)
+               
+               smsRegexpLabel = gtk.Label(_("Regular expression"))
+               smsRegexpLabel.set_alignment(0,0.6)
+               self.sms_box.add(smsRegexpLabel)
+               
+               self.sms_regexp = hildon.Entry(gtk.HILDON_SIZE_AUTO)
+               self.sms_regexp.set_text(config[17])
+               self.sms_box.add(self.sms_regexp)
+
+               self.sms_timeout = hildon.NumberEditor(0, 9999)
+               self.sms_timeout.set_value(config[18])
+               sms_timeout_box = gtk.HBox()
+               timeoutLabel = gtk.Label(_("Timeout"))
+               timeoutLabel.set_alignment(0,0.6)
+               secondsLabel = gtk.Label(_("seconds"))
+               timeoutLabel.set_size_request(140, -1)
+               self.sms_timeout.set_size_request(50, -1)
+               secondsLabel.set_size_request(40, -1)
+               sms_timeout_box.add(timeoutLabel)
+               sms_timeout_box.add(self.sms_timeout)
+               sms_timeout_box.add(secondsLabel)
+               self.sms_box.add(sms_timeout_box)
+               
+               vbox.add(self.sms_box)
+
+               vbox.add(gtk.Label(_("DO NOT CHANGE. Unspecified is what you want.")))
+               vbox.add(self.language)
+
+               self.show_all()
+               self.show_box_changed(None)
+               self.sms_box_changed(None)
+               self.parent
+
+       #============ Dialog helper functions =============
+       def on_show_phelp(self, widget):
+               dialog = pHelpDialog(_("Format help"), _("Reply would be passed to specified utility, output of utility would be shown to you on widget.\n       Format:\n% would be replaced by reply\n%N with N'th regexp matching group\n& would be replaced with sms content\n&N with N'th sms regexp group\n\\ invalidates special meaming of following symbol\n\" and ' work as usual\nspace delimits command line parameters of utility\n      Hint: use echo \"Your string %\" to prepend your string to reply."))
+               dialog.run()
+               dialog.destroy()
+
+       def on_show_bphelp(self, widget):
+               dialog = pHelpDialog(_("Format help"), _("Reply would be passed to specified utility, output of utility would be shown to you on banner.\n       Format:\n% would be replaced by reply\n%N with N'th regexp matching group\n& would be replaced with sms content\n&N with N'th sms regexp group\n\\ invalidates special meaming of following symbol\n\" and ' work as usual\nspace delimits command line parameters of utility\n      Hint: use echo \"Your string %\" to prepend your string to reply."))
+               dialog.run()
+               dialog.destroy()
+       
+       def on_show_chelp(self, widget):
+               dialog = pHelpDialog(_("Format help"), _("Reply would be passed to specified utility after parser utility. May be used for logging, statistics etc.\n       Format:\n% would be replaced by reply\n%N with N'th regexp matching group\n& would be replaced with sms content\n&N with N'th sms regexp group\n\\ invalidates special meaming of following symbol\n\" and ' work as usual\nspace delimits command line parameters of utility\n"))
+               dialog.run()
+               dialog.destroy()
+
+       def on_show_reghelp(self, widget):
+               dialog = pHelpDialog(_("Format help"), _("Standard python regexps. Use\n (.+?[\d\,\.]+)\n to delete everything after first number."))
+               dialog.run()
+               dialog.destroy()
+
+       def on_show_retryhelp(self, widget):
+               dialog = pHelpDialog(_("Format help"), _("Pauses between attemps (in seconds), delimited by -. For example 15-15-300 means \"In case of failure wait 15 seconds, try again, on failure wait 15 more secodns and try again, on failure make last attempt after 5 minutes\""))
+               dialog.run()
+               dialog.destroy()
+       
+       def on_show_number_help(self, widget):
+               dialog = pHelpDialog(_("Format help"), _("USSD number. To perform USSD menu navigation divide queries vith spacebars. For xample '*100# 1' means 1st entry in *100# menu."))
+               dialog.run()
+               dialog.destroy()
+       
+       def on_error_regexp(self, error):
+               dialog = pHelpDialog(_("Regexp syntax error"), error )
+               dialog.run()
+               dialog.destroy()
+
+       def on_error_ussd_number(self):
+               dialog = pHelpDialog(_("Incorrect USSD number"), _("USSD number should contain only digits, +, * or #") )
+               dialog.run()
+               dialog.destroy()
+
+       def on_error_retry_pattern(self):
+               dialog = pHelpDialog(_("Incorrect retry pattern"), _("Retry pattern should contain only numbers, delimited by -") )
+               dialog.run()
+               dialog.destroy()
+
+       def on_show_color_selection (self, event):
+               colorDialog = gtk.ColorSelectionDialog(_("Choose background color"))
+               colorDialog.colorsel.set_current_color(self.bg_color)
+               if colorDialog.run() == gtk.RESPONSE_OK :
+                       self.bg_color = colorDialog.colorsel.get_current_color()
+               colorDialog.destroy()
+
+       def on_show_text_color_selection (self, event):
+               colorDialog = gtk.ColorSelectionDialog(_("Choose text color"))
+               colorDialog.colorsel.set_current_color(self.text_color)
+               if colorDialog.run() == gtk.RESPONSE_OK :
+                       self.text_color = colorDialog.colorsel.get_current_color()
+               colorDialog.destroy()
+       
+       def on_show_font_selection (self, event):
+               fontDialog = gtk.FontSelectionDialog(_("Choose a font"))
+               fontDialog.set_font_name(self.font.to_string())
+
+               if fontDialog.run() != gtk.RESPONSE_OK :
+                       fontDialog.destroy()
+                       return
+
+               self.font = pango.FontDescription (fontDialog.get_font_name())
+               fontDialog.destroy()
+
+       def show_box_changed (self, event):
+               if self.show_box.get_active():
+                       self.b_parser.show()
+               else:
+                       self.b_parser.hide()
+
+       def sms_box_changed (self, event):
+               if self.sms_listener.get_active():
+                       self.sms_box.show()
+               else:
+                       self.sms_box.hide()
+
+def smart_split_string (str, reply1, reply2, reres1 = None, reres2 = None) :
+       word = ""
+       result = []
+       # Is simbol backslashed?
+       bs = 0
+       # Quotes: 1 - ", 2 - ', 0 - no quotes
+       qs = 0
+       # Read out number
+       num = -1
+       # Current substitution simbol
+       subst = ''
+
+       for i in range(len(str)) :
+               if num>= 0:
+                       if str[i] in ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"] :
+                               num *= 10
+                               num += int(str[i])
+                               continue
+                       else:
+                               if subst == '&':
+                                       if reres2 != None and num != 0:
+                                               word += reres2.group(num)
+                                       else:
+                                               word += reply2
+                               else:
+                                       if reres1 != None and num != 0:
+                                               word += reres1.group(num)
+                                       else:
+                                               word += reply1
+                               ws = 0
+                               num = -1
+                               subst = ''
+                               # Delete backslash if it delimites usual numbers from % or &
+                               if str[i] == '\\' and i < len(str)-1 and str[i+1] in ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"] :
+                                       continue
+               if bs == 0 and (str[i] == '"' and qs == 1 or str[i] == "'" and qs == 2) :
+                       qs = 0
+               elif bs == 0 and qs == 0 and (str[i] == '"' or str[i] == "'") :
+                       if str[i] == '"':
+                               qs = 1
+                       else :
+                               qs = 2
+               elif bs == 0 and str[i] == '\\' :
+                       bs = 1
+               elif bs == 0 and (str[i] == '%' or str[i] == '&') :
+                       subst = str[i]
+                       num = 0
+               else :
+                       if bs == 1 and str[i] != '\\' and str[i] != '"' and str[i] != "'" :
+                               word += "\\"
+                       if qs == 0 and (str[i] == " " or str[i] == "\t") :
+                               if word != "" :
+                                       result.append(word)
+                                       word = ""
+                       else :
+                               word += str[i]
+                       bs = 0 
+       
+       if subst == '&':
+               if reres2 != None and num != 0 and num != -1:
+                       word += reres2.group(num)
+               else:
+                       word += reply2
+       elif subst == '%':
+               if reres1 != None and num != 0 and num != -1:
+                       word += reres1.group(num)
+               else:
+                       word += reply1
+       if word != "" :
+               result.append(word)
+       return result 
 
 def check_regexp(regexp):
        try :
-           re.compile( regexp )
+               re.compile( regexp )
        except Exception, e:
-            on_error_regexp( str( e ) )
-            return 1
-       return 0
-
-#def timed_renewer(Thread):
-#    def __init__ (self, widget, period):
-#        self.widget = widget
-#        self.period = period
-#        self.version = widget.timerversion
-#
-#    def run (self):
-#        while widget.timerversion == version:
-#            ussd_renew(widget, None)
-#            time.sleep(period)
-
-def ussd_renew(widget, event):
-    if widget.process == None or widget.process.isAlive() == False :
-        widget.process = ussd_renewer (widget)
-        # See bug https://bugs.maemo.org/show_bug.cgi?id=7809
-        widget.process.run()
-
-class ussd_renewer ():#Thread):
-    def __init__ (self, widget):
-#        Thread.__init__ (self)
-        self.widget = widget
-        self.daemon = True
-    
-# stub while thread but is not fixed
-    def isAlive(self):
-        return False
-
-    def run(self):
-        widget = self.widget
-        config = get_config()
-        widget.processing = 1
-        last_text = widget.label.get_text()
-        widget.label.set_text("Processing")
-        widget.queue_draw()
-        gtk.main_iteration ()
-
-        if config :
-            p = Popen(['/usr/bin/ussdquery.py', config[0]], stdout=PIPE)
-            reply = p.communicate()[0].strip()
-            if reply == "" :
-                reply = "   Error   "
-                widget.error = 1
-                # Show previous text in 5 seconds
-                widget.timer = gobject.timeout_add (5000, error_return, widget, last_text)
-            else :
-                widget.error = 0
-                if config[1] != "":
-                    p = Popen(smart_split_string(config[1], reply), stdout=PIPE)
-                    reply = p.communicate(reply+"\n")[0].strip()
-                if config[2] != "":
-                    p = Popen(smart_split_string(config[2], reply))
-                if config[4] != "":
-                    try :
-                        r = re.match( config[4], reply ).group( 1 )
-                    except Exception, e:
-                        r = "Regexp Error: " + str( e )
-
-                    if r :
-                        reply = r
-        else :
-            reply = " Bad config "
-        widget.processing = 0
-        widget.label.set_text(reply)
-        widget.queue_draw()
-        gtk.main_iteration ()
-
-def error_return(widget, text) :
-    if widget.processing == 0 and widget.error == 1:
-        widget.label.set_text(text)
-    return False
-
-def on_show_settings(widget):
-    config = get_config()
-    if config == None :
-        config = ["", "", "", "", ""]
-
-    dialog = UssdConfigDialog(config)
-    dialog.run()
-
-    if check_regexp( dialog.regexp.get_text() ) :
-        return
-
-    set_config ([dialog.ussdNumber.get_text(), dialog.parser.get_text(), dialog.chain.get_text(), dialog.update_interval.get_text(), dialog.regexp.get_text()])
-
-# Doesn't work in hildon-home
-#   widget.timerversion += 1
-#   if dialog.update_interval.get_text() != "" :
-#       ussd_renewer (widget, 60000*int(dialog.update_interval.get_text()))
-  
-    dialog.destroy()
-    
-    if config == ["", "", "", "", ""] :
-        widget.label.set_text("Click to update")
-
-def on_show_phelp(widget):
-    dialog = pHelpDialog("Format help", "Reply would be passed to specified utility, output\n of utility would be shown to you.\n       Format:\n% would be replaced by reply\n\\ invalidates special meaming of following symbol\n\" and ' work as usual\nspace delimits command line parameters of utility")
-    dialog.run()
-    dialog.destroy()
-
-def on_show_chelp(widget):
-    dialog = pHelpDialog("Format help", "Reply would be passed to specified utility after\nparser utility. May be used for logging, statistics etc.\n       Format:\n% would be replaced by reply\n\\ invalidates special meaming of following symbol\n\" and ' work as usual\nspace delimits command line parameters of utility")
-    dialog.run()
-    dialog.destroy()
-
-def on_show_reghelp(widget):
-    dialog = pHelpDialog("Format help", "standard python regexps")
-    dialog.run()
-    dialog.destroy()
-
-def on_error_regexp(error):
-    dialog = pHelpDialog( "Regexp syntax error", error )
-    dialog.run()
-    dialog.destroy()
+                       return str(e)
+       return False
+
+def check_number(number):
+       for s in number :
+               if not (s in ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "+", "*", "#", " "]) :
+                       return False
+       return True
+
+#=============== The widget itself ================
 
 def get_color(logicalcolorname):
-    settings = gtk.settings_get_default()
-    color_style = gtk.rc_get_style_by_paths(settings, 'GtkButton', 'osso-logical-colors', gtk.Button)
-    return color_style.lookup_color(logicalcolorname)
+       settings = gtk.settings_get_default()
+       color_style = gtk.rc_get_style_by_paths(settings, 'GtkButton', 'osso-logical-colors', gtk.Button)
+       return color_style.lookup_color(logicalcolorname)
 
 class UssdWidgetPlugin(hildondesktop.HomePluginItem):
-    def __init__(self):
-        hildondesktop.HomePluginItem.__init__(self)
-        
-        self.process = None
-        self.timerversion = 0 # Because threads in pyton are crap
-        self.processing = 0
-        self.error = 0
-
-        colormap = self.get_screen().get_rgba_colormap()
-        self.set_colormap (colormap)
-
-        config = get_config()
-        
-        self.connect("button-press-event", ussd_renew)
-
-        self.label = gtk.Label()
-        self.label.set_padding(15, 10)
-        self.label.set_size_request(-1,-1)
-        self.set_size_request(-1,-1)
-
-        self.vbox = gtk.HBox()
-        self.vbox.add(self.label)
-        self.add(self.vbox)
-
-        self.set_settings(True)
-        self.connect("show-settings", on_show_settings)    
-
-        self.vbox.show_all()
-
-        if config :
-            self.label.set_label("Data not available")
-# Should be uncommented only after thread bug fixed
-#            ussd_renew (self, None)
-# Doesn't work in hildon-home
-#           if config[3] != "" :
-#               timed_renewer (self, 60000*int(config[3]))
-        else :
-            self.label.set_label("Configure me")
-
-    def _expose(self, event):
-        cr = self.window.cairo_create()
-
-        # draw rounded rect
-        width, height = self.allocation[2], self.allocation[3]
-
-        #/* a custom shape, that could be wrapped in a function */
-        x0 = 0   #/*< parameters like cairo_rectangle */
-        y0 = 0
-
-        radius = min(15, width/2, height/2)  #/*< and an approximate curvature radius */
-
-        x1 = x0 + width
-        y1 = y0 + height
-
-        cr.move_to  (x0, y0 + radius)
-        cr.arc (x0 + radius, y0 + radius, radius, 3.14, 1.5 * 3.14)
-        cr.line_to (x1 - radius, y0)
-        cr.arc (x1 - radius, y0 + radius, radius, 1.5 * 3.14, 0.0)
-        cr.line_to (x1 , y1 - radius)
-        cr.arc (x1 - radius, y1 - radius, radius, 0.0, 0.5 * 3.14)
-        cr.line_to (x0 + radius, y1)
-        cr.arc (x0 + radius, y1 - radius, radius, 0.5 * 3.14, 3.14)
-
-        cr.close_path ()
-
-        fg_color = get_color("ActiveTextColor")
-
-        if self.processing :
-            bg_color=fg_color
-        else :
-            bg_color=gtk.gdk.color_parse('#000000')
-
-        cr.set_source_rgba (bg_color.red / 65535.0, bg_color.green/65535.0, bg_color.blue/65535.0, 0.7)
-        cr.fill_preserve ()
-
-        if self.error :
-            cr.set_source_rgba (1.0, 0.0, 0.0, 0.5)
-        else :
-            cr.set_source_rgba (fg_color.red / 65535.0, fg_color.green / 65535.0, fg_color.blue / 65535.0, 0.7)
-        cr.stroke ()
-
-    def do_expose_event(self, event):
-        self.chain(event)
-        self._expose (event)
-        self.vbox.do_expose_event (self, event)
+       def __init__(self):
+               hildondesktop.HomePluginItem.__init__(self)
+       
+               self.processing = 0
+               self.bg_color=gtk.gdk.color_parse('#000000')
+               self.text_color=gtk.gdk.color_parse('#ffffff')
+               self.error = 0
+               self.timeout_version = 0
+
+               colormap = self.get_screen().get_rgba_colormap()
+               self.set_colormap (colormap)
+
+               self.controller = USSD_Controller(self)
+                
+# TODO Click event would be better
+               self.connect("button-press-event", self.controller.ussd_renew)
+
+               self.vbox = gtk.HBox()
+               self.add(self.vbox)
+
+               self.set_settings(True)
+               self.connect("show-settings", self.controller.on_show_settings)
+               self.label = gtk.Label("")
+               
+               self.vbox.add(self.label)
+               self.vbox.set_child_packing(self.label, False, False, 0, gtk.PACK_START)
+               self.label.set_padding(15, 10)
+               self.label.set_size_request(-1,-1)
+               self.set_size_request(-1,-1)
+               self.label.set_line_wrap (True)
+
+               self.vbox.show_all()
+
+       def do_show(self):
+               config = self.controller.read_config(self.get_applet_id())
+               self.set_width(config[5])
+               self.set_text(config[9])                
+               if config[6]:
+                       self.controller.ussd_renew(self, None)
+
+               self.label.modify_font(config[8])
+               self.controller.reset_timed_renew()
+               hildondesktop.HomePluginItem.do_show(self)
+       
+       def error_return (self):
+               if self.error == 1 and self.processing == 0:
+                       self.set_text(self.text)
+               return False
+
+       # showfor =
+       #       -1 - This is a permanent text message
+       #       0  - This is service message, but it shouldn't be hidden automatically
+       #       >0 - This is service message, show permament message after showfor milliseconds
+       def set_text(self, text, showfor=-1):
+               if showfor > 0 :
+                       # Show previous text after 5 seconds
+                       gobject.timeout_add (showfor, self.error_return)
+               else :
+                       if showfor == -1 :
+                               self.text = text
+               
+               config = self.controller.get_config()
+               self.label.set_text(text)
+
+       def get_text(self):
+               return self.text
+
+       def set_width(self, width):
+               if width != 0:
+                       self.label.set_width_chars (width)
+               else :
+                       self.label.set_width_chars(-1)
+
+       def set_bg_color(self, color):
+               self.bg_color = color
+
+       def get_bg_color(self):
+               return self.bg_color
+
+       def set_text_color(self, color):
+               self.label.modify_fg(gtk.STATE_NORMAL, color)           
+               self.text_color = color
+
+       def get_text_color(self):
+               return self.text_color
+
+       def _expose(self, event):
+               cr = self.window.cairo_create()
+
+               # draw rounded rect
+               width, height = self.label.allocation[2], self.label.allocation[3]
+
+               #/* a custom shape, that could be wrapped in a function */
+               x0 = 0   #/*< parameters like cairo_rectangle */
+               y0 = 0
+
+               radius = min(15, width/2, height/2)  #/*< and an approximate curvature radius */
+
+               x1 = x0 + width
+               y1 = y0 + height
+
+               cr.move_to  (x0, y0 + radius)
+               cr.arc (x0 + radius, y0 + radius, radius, 3.14, 1.5 * 3.14)
+               cr.line_to (x1 - radius, y0)
+               cr.arc (x1 - radius, y0 + radius, radius, 1.5 * 3.14, 0.0)
+               cr.line_to (x1 , y1 - radius)
+               cr.arc (x1 - radius, y1 - radius, radius, 0.0, 0.5 * 3.14)
+               cr.line_to (x0 + radius, y1)
+               cr.arc (x0 + radius, y1 - radius, radius, 0.5 * 3.14, 3.14)
+
+               cr.close_path ()
+
+               fg_color = get_color("ActiveTextColor")
+
+               if self.processing :
+                       bg_color=fg_color
+               else :
+                       bg_color=self.bg_color
+
+               cr.set_source_rgba (bg_color.red / 65535.0, bg_color.green/65535.0, bg_color.blue/65535.0, 0.7)
+               cr.fill_preserve ()
+
+               if self.error :
+                       cr.set_source_rgba (1.0, 0.0, 0.0, 0.5)
+               else :
+                       cr.set_source_rgba (fg_color.red / 65535.0, fg_color.green / 65535.0, fg_color.blue / 65535.0, 0.7)
+               cr.stroke ()
+
+       def do_expose_event(self, event):
+               self.chain(event)
+               self._expose (event)
+               self.vbox.do_expose_event (self, event)
 
 hd_plugin_type = UssdWidgetPlugin
-gtk.gdk.threads_init()
 
 # The code below is just for testing purposes.
 # It allows to run the widget as a standalone process.
 if __name__ == "__main__":
-    gobject.type_register(hd_plugin_type)
-    obj = gobject.new(hd_plugin_type, plugin_id="plugin_id")
-    obj.show_all()
-    gtk.gdk.threads_init()
-    gtk.main()
+       plugin_id = "ussd-widget.console"
+       if len(sys.argv) == 2:
+               try:
+                       plugin_id = "ussd-widget.desktop-"+str(int(sys.argv[1]))
+               except:
+                       print "Plugin id must be integer"
+                       sys.exit(-1)
+
+       import gobject
+       gobject.type_register(hd_plugin_type)
+       obj = gobject.new(hd_plugin_type, plugin_id=plugin_id)
+       obj.show_all()
+       gtk.main()