New release.
[ussd-widget] / ussd-widget / src / usr / lib / hildon-desktop / ussd-widget.py
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
3 import gobject
4 import gtk
5 import hildon
6 import hildondesktop
7 import os
8 import cairo
9 import pango
10 import time
11 import re
12 import gettext
13 import fcntl
14 from subprocess import *
15
16 try :
17         t = gettext.translation('ussd-widget', '/usr/share/locale')
18         _ = t.ugettext
19 except IOError:
20         print "Translation file for your language not found"
21         def retme(arg):
22                 return arg
23         _ = retme
24
25 ussd_languages = ["German", "English", "Italian", "French", "Spanish", "Dutch", "Swedish", "Danish", "Portuguese", "Finnish", "Norwegian", "Greek", "Turkish", "Reserved1", "Reserved2", "Unspecified"]
26
27 # TODO Cutt off too long messages and show them in separate dialog
28 # how TODO widget vertical minimum size policy
29 # TODO interface for queryng from scripts. For example query from one widget can change color of another widget
30
31 class USSD_Controller:
32         def __init__( self, widget ) :
33                 self.widget = widget
34                 # number, parser, chain, interval, regexp, width, execute_at_start, retry pattern, font, [name, always show], language 
35                 self.default_config = ["", "", "", 0, "", 0, True, [], pango.FontDescription("Nokia Sans 18"), [_("Click to update"), False], 15]
36                 self.config = self.default_config
37                 self.timeout_version = 0
38                 self.retry_version = 0
39                 self.retry_state = 0
40
41         def save_config( self ) :
42                 configname = os.getenv("HOME")+"/.ussdWidget.conf"
43                 # Aquire lock
44                 lockf = open(configname+".lock", 'a')
45                 fcntl.flock(lockf,fcntl.LOCK_EX)
46
47                 oldconfig=""
48                 try:
49                         fconfig = open(configname,"r")
50                         #Read configuration of other instances
51                         my_section = False
52                         for line in fconfig :
53                                 if line[0] == '%':
54                                         my_section = line[1:].strip() == self.id
55                                 if not my_section:
56                                         oldconfig += line
57                         fconfig.close()
58                 except:
59                         print _("Couldn't read previous config")
60
61                 fconfig = open(configname,"w")
62                 fconfig.seek(0)
63                 fconfig.write(oldconfig)
64                 fconfig.write("%"+self.id+"\n");
65                 fconfig.writelines(["# USSD query to be run by widget\n", "number="+self.config[0], "\n"])
66                 fconfig.writelines(["#Parser command\n", "parser="+self.config[1], "\n"])
67                 fconfig.writelines(["#Chain command\n", "chain="+self.config[2], "\n"])
68                 fconfig.writelines(["#Update interval in minutes\n", "interval="+str(self.config[3]), "\n"])
69                 fconfig.writelines(["#RegExp pattern\n", "regexp="+self.config[4], "\n"])
70                 fconfig.writelines(["#Widget width\n", "width="+str(self.config[5]), "\n"])
71                 fconfig.writelines(["#Execute query at start\n", "query_at_start="+str(self.config[6]), "\n"])
72                 fconfig.writelines(["#Retry pattern\n"])
73                 fconfig.write("retry=")
74                 first = True
75                 for i in self.config[7]:
76                         if not first:
77                                 fconfig.write ("-")
78                         fconfig.write(str(i))
79                         first = False
80                 fconfig.write("\n")
81                 fconfig.writelines(["#Font description\n", "font="+self.config[8].to_string(), "\n"])
82                 fconfig.writelines(["#Font color\n", "text_color="+self.widget.get_text_color().to_string(), "\n"])
83                 fconfig.writelines(["#Background color\n", "bg_color="+self.widget.get_bg_color().to_string(), "\n"])
84                 fconfig.writelines(["#Widget name\n", "name="+self.config[9][0], "\n"])
85                 fconfig.writelines(["#Always show widget name\n", "show_name="+str(self.config[9][1]), "\n"])
86                 fconfig.writelines(["#USSD reply language\n", "language="+str(self.config[10]), "\n"])
87                 fconfig.close()
88
89                 fcntl.flock(lockf,fcntl.LOCK_UN)
90                 lockf.close()
91
92         def get_config(self):
93                 return self.config
94
95         def read_config( self, id ):
96                 try :
97                         self.id = id
98                         config = open(os.getenv("HOME")+"/.ussdWidget.conf","r")
99
100                         error = False
101                         i = 0
102                         my_section = False
103                         for line in config :
104                                 i += 1 
105                                 if line[0] == '#':
106                                         continue
107                                 if line[0] == '%':
108                                         my_section = line[1:].strip() == id
109                                         continue
110
111                                 if not my_section:
112                                         # This is config for another instace
113                                         continue
114
115                                 line=line.split('=', 1)
116                                 
117                                 if len(line) != 2 :
118                                         error = True
119                                         print _("Error reading config on line %(line)d. = or # expected.")%{"line":i}
120                                         continue
121                                 if line[0] == "number" :
122                                         self.config[0] = line[1].strip()
123                                 elif line[0] == "parser" :
124                                         self.config[1] = line[1].strip()
125                                 elif line[0] == "chain" :
126                                         self.config[2] = line[1].strip()
127                                 elif line[0] == "interval" :
128                                         try:
129                                                 self.config[3] = int(line[1].strip())
130                                         except:
131                                                 error = True
132                                                 print _("Error reading config on line %(line)d. Integer expected.")%{"line":i}
133                                                 continue
134                                 elif line[0] == "regexp" :
135                                         self.config[4] = line[1].strip()
136                                 elif line[0] == "width" :
137                                         try:
138                                                 self.config[5] = int(line[1].strip())
139                                         except:
140                                                 error = True
141                                                 print _("Error reading config on line %(line)d. Integer expected.")%{"line":i}
142                                                 continue
143                                 elif line[0] == "query_at_start" :
144                                         if line[1].strip() == "True" :
145                                                 self.config[6] = True
146                                         else :
147                                                 self.config[6] = False
148                                 elif line[0] == "retry" :
149                                         line[1] = line[1].strip()
150                                         if line[1] != "":
151                                                 line[1] = line[1].split("-")
152                                                 i = 0
153                                                 while i < len(line[1]) :
154                                                         try:
155                                                                 line[1][i] = int(line[1][i])
156                                                         except:
157                                                                 error = True
158                                                                 print _("Error reading config on line %(line)d. Integer expected.")%{"line":i}
159                                                         i += 1
160                                                 self.config[7] = line[1]
161                                         else:
162                                                 self.config[7] = []
163                                                 continue
164                                 elif line[0] == "font" :
165                                         try:
166                                                 self.config[8] = pango.FontDescription(line[1].strip())
167                                         except:
168                                                 error = True
169                                                 print _("Error reading config on line %(line)d. Pango font description expected.")%{"line":i}
170                                                 continue
171                                 elif line[0] == "bg_color" :
172                                         try:
173                                                 self.widget.set_bg_color(gtk.gdk.color_parse(line[1].strip()))
174                                         except:
175                                                 error = True
176                                                 print _("Error reading config on line %(line)d. Expected color definition.")%{"line":i}
177                                 elif line[0] == "text_color" :
178                                         try:
179                                                 self.widget.set_text_color(gtk.gdk.color_parse(line[1].strip()))
180                                         except:
181                                                 error = True
182                                                 print _("Error reading config on line %(line)d. Expected color definition.")%{"line":i}
183                                 elif line[0] == "name" :
184                                         self.config[9][0] = line[1].strip()
185                                 elif line[0] == "show_name" :
186                                         if line[1].strip() == "True" :
187                                                 self.config[9][1] = True
188                                         else :
189                                                 self.config[9][1] = False
190                                 elif line[0] == "language" :
191                                         try:
192                                                 if int(line[1].strip()) >=0 and int(line[1].strip()) < len(ussd_languages):
193                                                         self.config[10] = int(line[1].strip())
194                                                 else:
195                                                         error = True
196                                                         print _("Error reading config on line %(line)d. Unknown language code.")%{"line":i}
197                                         except:
198                                                 error = True
199                                                 print _("Error reading config on line %(line)d. Integer expected.")%{"line":i}
200                                 else :
201                                         error = True
202                                         print _("Error reading config on line %(line)d. Unexpected variable: ")%{"line":i}+line[0]
203                                         continue 
204
205                         config.close()
206
207                         if error :
208                                 self.widget.error = 1
209                                 self.widget.set_text (_("Config error"), 5000)  
210
211                         return self.config
212                 except  IOError:
213                         self.widget.error = 1
214                         self.widget.set_text (_("Config error"), 0)
215                         print _("IO error while reading config")
216
217                         return self.default_config
218
219         def on_show_settings( self, widget ) :
220                 dialog = UssdConfigDialog(self.config, self.widget.get_bg_color(), self.widget.get_text_color())
221
222                 while True:
223                         if dialog.run() != gtk.RESPONSE_OK :
224                                 dialog.destroy()
225                                 return
226
227                         test = check_regexp(dialog.regexp.get_text()) 
228                         if test :
229                                 dialog.on_error_regexp(test)
230                                 continue
231
232                         # Check, that we have ussd number
233                         if not check_number(dialog.ussdNumber.get_text()):
234                                 dialog.on_error_ussd_number()
235                                 continue
236
237                         # Parse retry pattern
238                         retry = dialog.retryEdit.get_text().strip()
239                         if retry != "" :
240                                 retry = retry.split("-")
241                                 i = 0
242                                 while i < len(retry) :
243                                         try:
244                                                 retry[i] = int(retry[i])
245                                         except:
246                                                 dialog.on_error_retry_pattern()
247                                                 break 
248                                         i += 1
249                         
250                                 if i < len(retry):
251                                         continue
252                         else :
253                                 retry = []
254
255                         break
256
257                 self.config = [
258                         dialog.ussdNumber.get_text(), 
259                         dialog.parser.get_text(), 
260                         dialog.chain.get_text(), 
261                         dialog.update_interval.get_value(), 
262                         dialog.regexp.get_text(),
263                         dialog.widthEdit.get_value(),
264                         dialog.query_at_start.get_active(),
265                         retry,
266                         dialog.font,
267                         [dialog.wname.get_text(), dialog.show_name.get_active()],
268                         dialog.language.get_active()
269                 ]
270
271                 widget.set_bg_color(dialog.bg_color)
272                 widget.set_text_color(dialog.text_color)
273
274                 self.save_config()
275
276                 widget.set_width(self.config[5])        
277                 self.reset_timed_renew()
278                 self.widget.label.modify_font(self.config[8])
279
280                 dialog.destroy()
281
282                 # Before running this function widget wasn't configured
283                 if self.config == self.default_config:
284                         self.widget.set_text(_("Click to update"))
285                 return
286
287         def callback_ussd_data( self, source, condition ):
288                 if condition == gobject.IO_IN or condition == gobject.IO_PRI :
289                         data = source.read( )
290                         if len( data ) > 0 :
291                                 self.cb_reply += data
292                                 return True
293                         else :
294                                 self.cb_ready = 1
295                                 return False
296
297                 elif condition == gobject.IO_HUP or condition == gobject.IO_ERR :
298                         self.cb_ready = 1
299                         self.process_reply()
300                         return False
301
302                 print (_("serious problems in program logic"))
303                 # This will force widget to show error message
304                 self.cb_reply = ""
305                 self.cb_ready = 1
306                 self.process_reply()
307                 return False
308
309
310         def call_external_script( self, ussd_code, language ):
311                 self.cb_ready = 0
312                 self.cb_reply = "";
313                 p = Popen(['/usr/bin/ussdquery.py', ussd_code, ussd_languages[language]], stdout=PIPE)
314                 gobject.io_add_watch( p.stdout, gobject.IO_IN | gobject.IO_PRI | gobject.IO_HUP | gobject.IO_ERR , self.callback_ussd_data )
315
316         def ussd_renew(self, widget, event):
317                 if self.widget.processing == 0:
318                         if self.config :
319                                 widget.processing = 1
320                                 widget.set_text(_("Processing"), 0)
321                                 self.call_external_script( self.config[0], self.config[10] )
322                         else :
323                                 widget.processing = 0
324                                 widget.error = 1
325                                 widget.set_text(_("No config"), 0)
326
327         def process_reply( self ):
328                 reply = self.cb_reply.strip()
329                 
330                 if reply == "" :
331                         self.widget.error = 1
332                         self.widget.set_text (_("Error"), 5000)
333                         if self.retry_state == len(self.config[7]):
334                                 self.retry_version += 1
335                                 self.retry_state = 0
336                         else :
337                                 self.retry_timer = gobject.timeout_add (1000*self.config[7][self.retry_state], self.retry_renew, self.retry_version)
338                                 self.retry_state += 1
339                 else :
340                         self.widget.error = 0
341                         # Pass to parser
342                         if self.config[1] != "":
343                                 p = Popen(smart_split_string(self.config[1], reply), stdout=PIPE)
344                                 reply = p.communicate(reply+"\n")[0].strip()
345                         # Pass to chain
346                         if self.config[2] != "":
347                                 p = Popen(smart_split_string(self.config[2], reply))
348                         # Apply regexp
349                         if self.config[4] != "":
350                                 try :
351                                         r = re.match( self.config[4], reply ).group( 1 )
352                                 except Exception, e:
353                                         r = _("Regexp Error: ") + str( e ) + "\n" + reply
354
355                                 if r :
356                                         reply = r
357                         self.widget.set_text(reply)
358                 self.widget.processing = 0
359
360         def timed_renew(self, version):
361                 if version < self.timeout_version :
362                         return False
363                 self.ussd_renew(self.widget, None)
364                 return True
365
366         def retry_renew(self,version):
367                 if self.widget.error == 0 or self.widget.processing == 1 or version < self.retry_version :
368                         return False
369                 self.ussd_renew(self.widget, None)
370                 return False
371
372         def reset_timed_renew (self) :
373                 self.timeout_version += 1
374                 if self.config[3] != 0 :
375                         self.timer = gobject.timeout_add (60000*self.config[3], self.timed_renew, self.timeout_version)
376
377 class pHelpDialog(gtk.Dialog):
378         def __init__(self, heading, text):
379                 gtk.Dialog.__init__(self, heading, None,
380                         gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_NO_SEPARATOR,
381                         (_("OK").encode("utf-8"), gtk.RESPONSE_OK))
382                 label = gtk.Label(text)
383                 label.set_line_wrap (True)
384                 self.vbox.add(label)
385                 self.show_all()
386                 self.parent
387
388 class UssdConfigDialog(gtk.Dialog):
389         def __init__(self, config, bg_color, text_color):
390                 gtk.Dialog.__init__(self, _("USSD widget"), None, 
391                         gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_NO_SEPARATOR,
392                         (_("Save").encode("utf-8"), gtk.RESPONSE_OK))
393
394                 self.font = config[8]
395                 self.bg_color = bg_color
396                 self.text_color = text_color
397
398                 self.set_size_request(-1, 400)
399                 self.ussdNumber = hildon.Entry(gtk.HILDON_SIZE_AUTO)
400                 self.ussdNumber.set_text(config[0])
401                 self.parser = hildon.Entry(gtk.HILDON_SIZE_AUTO)
402                 self.parser.set_text(config[1])
403                 self.chain = hildon.Entry(gtk.HILDON_SIZE_AUTO)
404                 self.chain.set_text(config[2])
405                 self.update_interval = hildon.NumberEditor(0, 9999)
406                 self.update_interval.set_value(config[3])
407                 self.regexp = hildon.Entry(gtk.HILDON_SIZE_AUTO)
408                 self.regexp.set_text(config[4])
409                 self.widthEdit = hildon.NumberEditor(0, 1000)
410                 self.widthEdit.set_value(config[5])
411                 self.retryEdit = hildon.Entry(gtk.HILDON_SIZE_AUTO)
412
413
414                 #selector = hildon.hildon_touch_selector_new_text()
415                 selector = hildon.TouchSelector(text=True)
416                 for i in ussd_languages:
417                         selector.append_text(i)
418                 self.language = hildon.PickerButton(gtk.HILDON_SIZE_AUTO, hildon.BUTTON_ARRANGEMENT_HORIZONTAL)
419                 self.language.set_selector(selector)
420                 self.language.set_active(config[10])
421                 self.language.set_title(_("USSD reply language"))
422                 self.language.set_size_request(-1, -1)
423
424                 self.wname = hildon.Entry(gtk.HILDON_SIZE_AUTO)
425                 self.wname.set_text(config[9][0])
426                 self.show_name = gtk.CheckButton(_("Always show name"))
427                 self.show_name.set_active(config[9][1])
428
429                 text = ""
430                 for i in config[7]:
431                         if text != "":
432                                 text += "-"
433                         text += str(i)
434                 self.retryEdit.set_text(text)
435
436                 self.query_at_start = gtk.CheckButton(_("Execute query on start"))
437                 self.query_at_start.set_active(config[6])
438
439                 self.fontButton = gtk.Button(_("Font"))
440                 self.fontButton.connect("clicked", self.on_show_font_selection)
441
442                 self.colorButton = gtk.Button(_("Background color"))
443                 self.colorButton.connect("clicked", self.on_show_color_selection)
444                 self.textColorButton = gtk.Button(_("Text color"))
445                 self.textColorButton.connect("clicked", self.on_show_text_color_selection)
446                 
447                 phelp = gtk.Button("?")
448                 phelp.connect("clicked", self.on_show_phelp)
449
450                 chelp = gtk.Button("?")
451                 chelp.connect("clicked", self.on_show_chelp)
452
453                 reghelp = gtk.Button("?")
454                 reghelp.connect("clicked", self.on_show_reghelp)
455
456                 retryhelp = gtk.Button("?")
457                 retryhelp.connect("clicked", self.on_show_retryhelp)
458
459                 area = hildon.PannableArea()
460                 self.vbox.add(area)
461                 vbox = gtk.VBox()
462                 area.add_with_viewport(vbox)
463                 
464                 numberBox = gtk.HBox()
465                 numberLabel = gtk.Label(_("USSD number"))
466                 numberLabel.set_alignment(0,0)
467                 numberLabel.set_size_request(100, -1)
468                 self.ussdNumber.set_size_request(200, -1)
469                 numberBox.add(numberLabel)
470                 numberBox.add(self.ussdNumber)
471                 vbox.add(numberBox)
472
473                 vbox.add(self.language)
474
475                 vbox.add(self.query_at_start)
476
477                 nameBox = gtk.HBox()
478                 self.show_name.set_size_request(100, -1)
479                 self.wname.set_size_request(100, -1)
480                 nameBox.add(self.show_name)
481                 nameBox.add(self.wname)
482                 vbox.add(nameBox)
483
484                 parserBox = gtk.HBox()
485                 parserLabel = gtk.Label(_("Parser"))
486                 parserLabel.set_alignment(0,0)
487                 parserLabel.set_size_request(200, -1)
488                 phelp.set_size_request(10, -1)
489                 parserBox.add(parserLabel)
490                 parserBox.add(phelp)
491                 vbox.add(parserBox)
492                 vbox.add(self.parser)
493
494                 chainBox = gtk.HBox()
495                 chainLabel = gtk.Label(_("Chain"))
496                 chainLabel.set_alignment(0,0)
497                 chainLabel.set_size_request(200, -1)
498                 chelp.set_size_request(10, -1)
499                 chainBox.add(chainLabel)
500                 chainBox.add(chelp)
501                 vbox.add(chainBox)
502                 vbox.add(self.chain)
503
504                 regexpBox = gtk.HBox()
505                 regexpLabel = gtk.Label(_("RegExp"))
506                 regexpLabel.set_alignment(0,0)
507                 regexpLabel.set_size_request(200, -1)
508                 reghelp.set_size_request(10, -1)
509                 regexpBox.add(regexpLabel)
510                 regexpBox.add(reghelp)
511                 vbox.add(regexpBox)
512                 vbox.add(self.regexp)           
513
514                 widthBox = gtk.HBox()
515                 widthLabel = gtk.Label(_("Max. width"))
516                 widthLabel.set_alignment(0,0)
517                 symbolsLabel = gtk.Label(_("symbols"))
518                 widthLabel.set_size_request(140, -1)
519                 self.widthEdit.set_size_request(50, -1)
520                 symbolsLabel.set_size_request(40,-1)
521                 widthBox.add(widthLabel)
522                 widthBox.add(self.widthEdit)
523                 widthBox.add(symbolsLabel)
524                 vbox.add(widthBox)
525
526                 updateBox = gtk.HBox()
527                 updateLabel = gtk.Label(_("Update every"))
528                 updateLabel.set_alignment(0,0)
529                 minutesLabel = gtk.Label(_("minutes"))
530                 updateLabel.set_size_request(140, -1)
531                 self.update_interval.set_size_request(50, -1)
532                 minutesLabel.set_size_request(40, -1)
533                 updateBox.add(updateLabel)
534                 updateBox.add(self.update_interval)
535                 updateBox.add(minutesLabel)
536                 vbox.add(updateBox)
537
538                 retryBox = gtk.HBox()
539                 retryLabel = gtk.Label(_("Retry pattern"))
540                 retryLabel.set_alignment(0,0)
541                 retryLabel.set_size_request(200, -1)
542                 retryhelp.set_size_request(10, -1)
543                 retryBox.add(retryLabel)
544                 retryBox.add(retryhelp)
545                 vbox.add(retryBox)
546                 vbox.add(self.retryEdit)                
547                 
548                 viewBox = gtk.HBox()
549                 viewBox.add(self.fontButton)
550                 viewBox.add(self.textColorButton)
551                 viewBox.add(self.colorButton)
552                 vbox.add(viewBox)
553
554                 self.show_all()
555                 self.parent
556
557         #============ Dialog helper functions =============
558         def on_show_phelp(self, widget):
559                 dialog = pHelpDialog(_("Format help"), _("Reply would be passed to specified utility, output 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"))
560                 dialog.run()
561                 dialog.destroy()
562
563         def on_show_chelp(self, widget):
564                 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\\ invalidates special meaning of following symbol\n\" and ' work as usual\nspace delimits command line parameters of utility"))
565                 dialog.run()
566                 dialog.destroy()
567
568         def on_show_reghelp(self, widget):
569                 dialog = pHelpDialog(_("Format help"), _("Standard python regexps. Use\n (.+?[\d\,\.]+)\n to delete everything after first number."))
570                 dialog.run()
571                 dialog.destroy()
572
573         def on_show_retryhelp(self, widget):
574                 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\""))
575                 dialog.run()
576                 dialog.destroy()
577         
578         def on_error_regexp(self, error):
579                 dialog = pHelpDialog(_("Regexp syntax error"), error )
580                 dialog.run()
581                 dialog.destroy()
582
583         def on_error_ussd_number(self):
584                 dialog = pHelpDialog(_("Incorrect USSD number"), _("USSD number should contain only digits, +, * or #") )
585                 dialog.run()
586                 dialog.destroy()
587
588         def on_error_retry_pattern(self):
589                 dialog = pHelpDialog(_("Incorrect retry pattern"), _("Retry pattern should contain only numbers, delimited by -") )
590                 dialog.run()
591                 dialog.destroy()
592
593         def on_show_color_selection (self, event):
594                 colorDialog = gtk.ColorSelectionDialog(_("Choose background color"))
595                 colorDialog.colorsel.set_current_color(self.bg_color)
596                 if colorDialog.run() == gtk.RESPONSE_OK :
597                         self.bg_color = colorDialog.colorsel.get_current_color()
598                 colorDialog.destroy()
599
600         def on_show_text_color_selection (self, event):
601                 colorDialog = gtk.ColorSelectionDialog(_("Choose text color"))
602                 colorDialog.colorsel.set_current_color(self.text_color)
603                 if colorDialog.run() == gtk.RESPONSE_OK :
604                         self.text_color = colorDialog.colorsel.get_current_color()
605                 colorDialog.destroy()
606         
607         def on_show_font_selection (self, event):
608                 fontDialog = gtk.FontSelectionDialog(_("Choose a font"))
609                 fontDialog.set_font_name(self.font.to_string())
610
611                 if fontDialog.run() != gtk.RESPONSE_OK :
612                         fontDialog.destroy()
613                         return
614
615                 self.font = pango.FontDescription (fontDialog.get_font_name())
616                 fontDialog.destroy()
617
618 def smart_split_string (str, query) :
619         word = ""
620         result = []
621         # Is simbol backslashed?
622         bs = 0
623         # Quotes: 1 - ", 2 - ', 0 - no quotes
624         qs = 0
625         for i in range(len(str)) :
626           if bs == 0 and (str[i] == '"' and qs == 1 or str[i] == "'" and qs == 2) :
627                   qs = 0
628           elif bs == 0 and qs == 0 and (str[i] == '"' or str[i] == "'") :
629                   if str[i] == '"':
630                           qs = 1
631                   else :
632                           qs = 2
633           elif bs == 0 and str[i] == '\\' :
634                   bs = 1
635           elif bs == 0 and str[i] == '%' :
636                   word += query
637                   ws = 0
638           else :
639                   if bs == 1 and str[i] != '\\' and str[i] != '"' and str[i] != "'" :
640                           word += "\\"
641                   if qs == 0 and (str[i] == " " or str[i] == "\t") :
642                           if word != "" :
643                                   result.append(word)
644                                   word = ""
645                   else :
646                           word += str[i]
647                   bs = 0 
648         if word != "" :
649                 result.append(word)
650         return result 
651
652 def check_regexp(regexp):
653         try :
654                 re.compile( regexp )
655         except Exception, e:
656                         return str(e)
657         return False
658
659 def check_number(number):
660         for s in number :
661                 if not (s in ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "+", "*", "#"]) :
662                         return False
663         return True
664
665 #=============== The widget itself ================
666
667 def get_color(logicalcolorname):
668         settings = gtk.settings_get_default()
669         color_style = gtk.rc_get_style_by_paths(settings, 'GtkButton', 'osso-logical-colors', gtk.Button)
670         return color_style.lookup_color(logicalcolorname)
671
672 class UssdWidgetPlugin(hildondesktop.HomePluginItem):
673         def __init__(self):
674                 hildondesktop.HomePluginItem.__init__(self)
675         
676                 self.processing = 0
677                 self.bg_color=gtk.gdk.color_parse('#000000')
678                 self.text_color=gtk.gdk.color_parse('#ffffff')
679                 self.error = 0
680                 self.timeout_version = 0
681
682                 colormap = self.get_screen().get_rgba_colormap()
683                 self.set_colormap (colormap)
684
685                 self.controller = USSD_Controller(self)
686                  
687 # TODO Click event would be better
688                 self.connect("button-press-event", self.controller.ussd_renew)
689
690                 self.vbox = gtk.HBox()
691                 self.add(self.vbox)
692
693                 self.set_settings(True)
694                 self.connect("show-settings", self.controller.on_show_settings)
695                 self.label = gtk.Label("")
696                 
697                 self.vbox.add(self.label)
698                 self.vbox.set_child_packing(self.label, False, False, 0, gtk.PACK_START)
699                 self.label.set_padding(15, 10)
700                 self.label.set_size_request(-1,-1)
701                 self.set_size_request(-1,-1)
702                 self.label.set_line_wrap (True)
703
704                 self.vbox.show_all()
705
706         def do_show(self):
707                 config = self.controller.read_config(self.get_applet_id())
708                 self.set_width(config[5])
709                 self.set_text("")               
710                 if config[6]:
711                         self.controller.ussd_renew(self, None)
712
713                 self.controller.reset_timed_renew()
714                 hildondesktop.HomePluginItem.do_show(self)
715         
716         def error_return (self):
717                 if self.error == 1 and self.processing == 0:
718                         self.set_text(self.text)
719                 return False
720
721         # showfor =
722         #       -1 - This is a permanent text message
723         #       0  - This is service message, but it shouldn't be hidden automatically
724         #       >0 - This is service message, show permament message after showfor milliseconds
725         def set_text(self, text, showfor=-1):
726                 if showfor > 0 :
727                         # Show previous text after 5 seconds
728                         gobject.timeout_add (showfor, self.error_return)
729                 else :
730                         if showfor == -1 :
731                                 self.text = text
732                 
733                 config = self.controller.get_config()
734                 if config[9][1] or text == "":
735                         text = config[9][0]+text
736                 self.label.set_text(text)
737
738         def get_text(self):
739                 return self.text
740
741         def set_width(self, width):
742                 if width != 0:
743                         self.label.set_width_chars (width)
744                 else :
745                         self.label.set_width_chars(-1)
746
747         def set_bg_color(self, color):
748                 self.bg_color = color
749
750         def get_bg_color(self):
751                 return self.bg_color
752
753         def set_text_color(self, color):
754                 self.label.modify_fg(gtk.STATE_NORMAL, color)           
755                 self.text_color = color
756
757         def get_text_color(self):
758                 return self.text_color
759
760         def _expose(self, event):
761                 cr = self.window.cairo_create()
762
763                 # draw rounded rect
764                 width, height = self.label.allocation[2], self.label.allocation[3]
765
766                 #/* a custom shape, that could be wrapped in a function */
767                 x0 = 0   #/*< parameters like cairo_rectangle */
768                 y0 = 0
769
770                 radius = min(15, width/2, height/2)  #/*< and an approximate curvature radius */
771
772                 x1 = x0 + width
773                 y1 = y0 + height
774
775                 cr.move_to  (x0, y0 + radius)
776                 cr.arc (x0 + radius, y0 + radius, radius, 3.14, 1.5 * 3.14)
777                 cr.line_to (x1 - radius, y0)
778                 cr.arc (x1 - radius, y0 + radius, radius, 1.5 * 3.14, 0.0)
779                 cr.line_to (x1 , y1 - radius)
780                 cr.arc (x1 - radius, y1 - radius, radius, 0.0, 0.5 * 3.14)
781                 cr.line_to (x0 + radius, y1)
782                 cr.arc (x0 + radius, y1 - radius, radius, 0.5 * 3.14, 3.14)
783
784                 cr.close_path ()
785
786                 fg_color = get_color("ActiveTextColor")
787
788                 if self.processing :
789                         bg_color=fg_color
790                 else :
791                         bg_color=self.bg_color
792
793                 cr.set_source_rgba (bg_color.red / 65535.0, bg_color.green/65535.0, bg_color.blue/65535.0, 0.7)
794                 cr.fill_preserve ()
795
796                 if self.error :
797                         cr.set_source_rgba (1.0, 0.0, 0.0, 0.5)
798                 else :
799                         cr.set_source_rgba (fg_color.red / 65535.0, fg_color.green / 65535.0, fg_color.blue / 65535.0, 0.7)
800                 cr.stroke ()
801
802         def do_expose_event(self, event):
803                 self.chain(event)
804                 self._expose (event)
805                 self.vbox.do_expose_event (self, event)
806
807 hd_plugin_type = UssdWidgetPlugin
808
809 # The code below is just for testing purposes.
810 # It allows to run the widget as a standalone process.
811 if __name__ == "__main__":
812         import gobject
813         gobject.type_register(hd_plugin_type)
814         obj = gobject.new(hd_plugin_type, plugin_id="plugin_id")
815         obj.show_all()
816         gtk.main()