2 # -*- coding: utf-8 -*-
4 ## This program is free software; you can redistribute it and/or modify
5 ## it under the terms of the GNU General Public License as published
6 ## by the Free Software Foundation; version 2 and higer.
8 ## Guseynov Alexey (kibergus bark-bark gmail.com) 2010
25 from dbus.mainloop.glib import DBusGMainLoop
27 # Would be truncated on every reboot, and shouldn't write
28 # anythong if things go right way so it is OK not to have logrotate
29 log = open("/var/log/ussd-widget.log", "w", 0)
30 print >> sys.stderr, "Writing log to /var/log/ussd-widget.log"
34 t = gettext.translation('ussd-widget', '/usr/share/locale')
37 print >> log, "Translation file for your language not found"
42 ussd_languages = ["German", "English", "Italian", "French", "Spanish", "Dutch", "Swedish", "Danish", "Portuguese", "Finnish", "Norwegian", "Greek", "Turkish", "Reserved1", "Reserved2", "Unspecified"]
43 ussd_languages_localized = [_("German"), _("English"), _("Italian"), _("French"), _("Spanish"), _("Dutch"), _("Swedish"), _("Danish"), _("Portuguese"), _("Finnish"), _("Norwegian"), _("Greek"), _("Turkish"), _("Reserved1"), _("Reserved2"), _("Unspecified")]
45 # TODO Cutt off too long messages and show them in separate dialog
46 # how TODO widget vertical minimum size policy
48 class USSD_Controller:
49 def __init__( self, widget ) :
51 # number, parser, chain, interval, regexp, width, execute_at_start,
52 # retry pattern, font, name, language, show_message_box,
53 # message_box_parser, additional arguments, regexp group,
54 # use SMS listener, SMS number, SMS regexp, SMS timeout
55 self.default_config = ["", "", "", 0, "", 0, True, [],\
56 pango.FontDescription("Nokia Sans 18"), _("Click to update"),\
57 15, False, "", "", 1, False, "", "", 60]
58 self.config = self.default_config
59 self.timeout_version = 0
60 self.retry_version = 0
65 def save_config( self ) :
66 configname = os.getenv("HOME")+"/.ussdWidget.conf"
68 lockf = open(configname+".lock", 'a')
69 fcntl.flock(lockf,fcntl.LOCK_EX)
73 fconfig = open(configname,"r")
74 #Read configuration of other instances
78 my_section = line[1:].strip() == self.id
83 print >> log, _("Couldn't read previous config")
85 fconfig = open(configname,"w")
87 fconfig.write(oldconfig)
88 fconfig.write("%"+self.id+"\n");
89 fconfig.writelines(["#USSD query to be run by widget\n", "number="+self.config[0], "\n"])
90 fconfig.writelines(["#Parser command for widget\n", "parser="+self.config[1], "\n"])
91 fconfig.writelines(["#Parser command for banner\n", "parser_box="+self.config[12], "\n"])
92 fconfig.writelines(["#Chain command\n", "chain="+self.config[2], "\n"])
93 fconfig.writelines(["#Update interval in minutes\n", "interval="+str(self.config[3]), "\n"])
94 fconfig.writelines(["#RegExp pattern\n", "regexp="+self.config[4], "\n"])
95 fconfig.writelines(["#Widget width\n", "width="+str(self.config[5]), "\n"])
96 fconfig.writelines(["#Execute query at start\n", "query_at_start="+str(self.config[6]), "\n"])
97 fconfig.writelines(["#Retry pattern\n"])
98 fconfig.write("retry=")
100 for i in self.config[7]:
103 fconfig.write(str(i))
106 fconfig.writelines(["#Font description\n", "font="+self.config[8].to_string(), "\n"])
107 fconfig.writelines(["#Font color\n", "text_color="+self.widget.get_text_color().to_string(), "\n"])
108 fconfig.writelines(["#Background color\n", "bg_color="+self.widget.get_bg_color().to_string(), "\n"])
109 fconfig.writelines(["#Widget name\n", "name="+self.config[9], "\n"])
110 fconfig.writelines(["#Show banner\n", "show_box="+str(self.config[11]), "\n"])
111 fconfig.writelines(["#USSD reply language\n", "language="+str(self.config[10]), "\n"])
112 fconfig.writelines(["#Additional ussdquery.py arguments\n", "args="+self.config[13], "\n"])
113 fconfig.writelines(["#Regexp matching group\n", "reggroup="+str(self.config[14]), "\n"])
114 fconfig.writelines(["#Use SMS listener\n", "listen_sms="+str(self.config[15]), "\n"])
115 fconfig.writelines(["#Number,from which SMS should come\n", "sms_number="+self.config[16], "\n"])
116 fconfig.writelines(["#SMS RegExp pattern\n", "sms_regexp="+self.config[17], "\n"])
117 fconfig.writelines(["#SMS timeout\n", "sms_timeout="+str(self.config[18]), "\n"])
120 fcntl.flock(lockf,fcntl.LOCK_UN)
123 def get_config(self):
126 def read_config( self, id ):
129 config = open(os.getenv("HOME")+"/.ussdWidget.conf","r")
139 my_section = line[1:].strip() == id
143 # This is config for another instace
146 line=line.split('=', 1)
150 print >> log, _("Error reading config on line %(line)d. = or # expected.")%{"line":i}
152 if line[0] == "number" :
153 self.config[0] = line[1].strip()
154 elif line[0] == "parser" :
155 self.config[1] = line[1].strip()
156 elif line[0] == "parser_box" :
157 self.config[12] = line[1].strip()
158 elif line[0] == "chain" :
159 self.config[2] = line[1].strip()
160 elif line[0] == "interval" :
162 self.config[3] = int(line[1].strip())
165 print >> log, _("Error reading config on line %(line)d. Integer expected.")%{"line":i}
167 elif line[0] == "regexp" :
168 self.config[4] = line[1].strip()
169 elif line[0] == "width" :
171 self.config[5] = int(line[1].strip())
174 print >> log, _("Error reading config on line %(line)d. Integer expected.")%{"line":i}
176 elif line[0] == "query_at_start" :
177 if line[1].strip() == "True" :
178 self.config[6] = True
180 self.config[6] = False
181 elif line[0] == "retry" :
182 line[1] = line[1].strip()
184 line[1] = line[1].split("-")
186 while i < len(line[1]) :
188 line[1][i] = int(line[1][i])
191 print >> log, _("Error reading config on line %(line)d. Integer expected.")%{"line":i}
193 self.config[7] = line[1]
197 elif line[0] == "font" :
199 self.config[8] = pango.FontDescription(line[1].strip())
202 print >> log, _("Error reading config on line %(line)d. Pango font description expected.")%{"line":i}
204 elif line[0] == "bg_color" :
206 self.widget.set_bg_color(gtk.gdk.color_parse(line[1].strip()))
209 print >> log, _("Error reading config on line %(line)d. Expected color definition.")%{"line":i}
210 elif line[0] == "text_color" :
212 self.widget.set_text_color(gtk.gdk.color_parse(line[1].strip()))
215 print >> log, _("Error reading config on line %(line)d. Expected color definition.")%{"line":i}
216 elif line[0] == "name" :
217 self.config[9] = line[1].strip()
218 elif line[0] == "show_box" :
219 if line[1].strip() == "True" :
220 self.config[11] = True
222 self.config[11] = False
223 elif line[0] == "language" :
225 if int(line[1].strip()) >=0 and int(line[1].strip()) < len(ussd_languages):
226 self.config[10] = int(line[1].strip())
229 print >> log, _("Error reading config on line %(line)d. Unknown language code.")%{"line":i}
232 print >> log, _("Error reading config on line %(line)d. Integer expected.")%{"line":i}
233 elif line[0] == "args" :
234 self.config[13] = line[1].strip()
235 elif line[0] == "reggroup" :
237 self.config[14] = int(line[1].strip())
240 print >> log, _("Error reading config on line %(line)d. Integer expected.")%{"line":i}
242 elif line[0] == "listen_sms" :
243 if line[1].strip() == "True" :
244 self.config[15] = True
246 self.config[15] = False
247 elif line[0] == "sms_number" :
248 self.config[16] = line[1].strip()
249 elif line[0] == "sms_regexp" :
250 self.config[17] = line[1].strip()
251 elif line[0] == "sms_timeout" :
253 self.config[18] = int(line[1].strip())
256 print >> log, _("Error reading config on line %(line)d. Integer expected.")%{"line":i}
260 print >> log, _("Error reading config on line %(line)d. Unexpected variable: ")%{"line":i}+line[0]
266 self.widget.error = 1
267 self.widget.set_text (_("Config error"), 5000)
271 self.widget.error = 1
272 self.widget.set_text (_("Config error"), 0)
273 print >> log, _("IO error while reading config")
275 return self.default_config
277 def on_show_settings( self, widget ) :
278 dialog = UssdConfigDialog(self.config, self.widget.get_bg_color(), self.widget.get_text_color(), self.id)
281 if dialog.run() != gtk.RESPONSE_OK :
285 test = check_regexp(dialog.regexp.get_text())
287 dialog.on_error_regexp(test)
290 # Check, that we have ussd number
291 if not check_number(dialog.ussdNumber.get_text()):
292 dialog.on_error_ussd_number()
295 if not check_number(dialog.sms_number.get_text()):
296 dialog.on_error_sms_number()
299 # Parse retry pattern
300 retry = dialog.retryEdit.get_text().strip()
302 retry = retry.split("-")
304 while i < len(retry) :
306 retry[i] = int(retry[i])
308 dialog.on_error_retry_pattern()
320 dialog.ussdNumber.get_text(),
321 dialog.parser.get_text(),
322 dialog.chain.get_text(),
323 dialog.update_interval.get_value(),
324 dialog.regexp.get_text(),
325 dialog.widthEdit.get_value(),
326 dialog.query_at_start.get_active(),
329 dialog.wname.get_text(),
330 dialog.language.get_active(),
331 dialog.show_box.get_active(),
332 dialog.b_parser.get_text(),
333 dialog.args.get_text(),
334 dialog.reggroup.get_value(),
335 dialog.sms_listener.get_active(),
336 dialog.sms_number.get_text(),
337 dialog.sms_regexp.get_text(),
338 dialog.sms_timeout.get_value()
341 widget.set_bg_color(dialog.bg_color)
342 widget.set_text_color(dialog.text_color)
346 widget.set_width(self.config[5])
347 self.reset_timed_renew()
348 self.widget.label.modify_font(self.config[8])
352 # Before running this function widget wasn't configured
353 if self.config == self.default_config:
354 self.widget.set_text(_("Click to update"))
357 def handle_sms(self, pdumsg, msgcenter, message, sendernumber):
358 # Timeout was recieved first
362 if self.config[16] == "" or self.config[16] == sendernumber:
363 pdu = gsmdecode.decode_pdu (pdumsg)
365 self.sms_reply += pdu['user_data']
367 if self.config[17] == "" or re.search( self.config[17], message, re.MULTILINE | re.UNICODE ):
368 self.sms_ready = True
369 self.sms_signal.remove()
372 def callback_ussd_data(self, source, condition, process):
373 if condition == gobject.IO_IN or condition == gobject.IO_PRI:
375 self.cb_reply += data
378 if condition == gobject.IO_ERR:
379 print >> log, "Communication error occured"
380 # This will force widget to show error message
383 if condition == gobject.IO_HUP:
384 # Pipe is broken, so ussd-query.py is already terminating
385 # and we wouldn't wait fot it too long
386 retcode = process.wait()
390 self.ussd_ready = True
394 def callback_ussd_error(self, source, condition):
395 if condition == gobject.IO_IN or condition == gobject.IO_PRI:
396 self.error_message += source.read()
398 if self.error_message != "":
399 print >> log, self.error_message
401 def call_external_script(self, ussd_code, language):
403 process = subprocess.Popen(
404 ['/usr/bin/ussdquery.py', ussd_code, "-l", ussd_languages[language]] +\
405 smart_split_string(self.config[13],"%","&"),
406 stdin=subprocess.PIPE,
407 stdout=subprocess.PIPE,
408 stderr=subprocess.PIPE)
409 process.stdin.close()
410 gobject.io_add_watch(
411 process.stdout, gobject.IO_IN | gobject.IO_PRI | gobject.IO_HUP | gobject.IO_ERR,
412 self.callback_ussd_data,
414 self.error_message = "";
415 gobject.io_add_watch(
416 process.stderr, gobject.IO_IN | gobject.IO_PRI | gobject.IO_HUP | gobject.IO_ERR,
417 self.callback_ussd_error)
419 def ussd_renew(self, widget, event):
420 if self.widget.processing == 0:
422 widget.processing = 1
423 widget.set_text(_("Processing"), 0)
425 self.ussd_ready = False
426 self.sms_ready = False
430 self.sms_counter += 1
431 self.retry_timer = gobject.timeout_add (1000*self.config[18], self.sms_timeout, self.sms_counter)
433 self.bus = dbus.SystemBus()
434 self.sms_signal = self.bus.add_signal_receiver(self.handle_sms, path='/com/nokia/phone/SMS', dbus_interface='Phone.SMS', signal_name='IncomingSegment')
436 self.call_external_script( self.config[0], self.config[10] )
438 widget.processing = 0
440 widget.set_text(_("No config"), 0)
442 def process_reply( self ):
443 if not self.ussd_ready or not self.sms_ready and self.config[15]:
446 reply = self.cb_reply.strip()
447 sms_reply = self.sms_reply.strip()
449 if reply == "" or self.config[15] and sms_reply == "" :
450 self.widget.error = 1
451 self.widget.set_text (_("Error"), 5000)
452 if self.retry_state == len(self.config[7]):
453 self.retry_version += 1
456 self.retry_timer = gobject.timeout_add (1000*self.config[7][self.retry_state], self.retry_renew, self.retry_version)
457 self.retry_state += 1
459 self.widget.error = 0
461 reresult1 = reresult2 = None
462 if self.config[4] != "":
463 reresult1 = re.search( self.config[4], reply, re.MULTILINE | re.UNICODE )
464 if self.config[17] != "":
465 reresult2 = re.search( self.config[17], sms_reply, re.MULTILINE | re.UNICODE )
466 w_reply = b_reply = reply
467 if self.widget.error == 0:
469 if self.config[12] != "" and self.config[11]:
471 p = subprocess.Popen(smart_split_string(self.config[12], reply, sms_reply, reresult1, reresult2), stdout=subprocess.PIPE)
472 b_reply = p.communicate()[0].strip()
474 print >> log, _("Couldn't exec banner parser:")+str(e)
475 self.widget.error = 1
477 if self.config[4] != "":
479 b_reply = reresult1.group( self.config[14] )
481 self.widget.error = 1
482 b_reply = _("Group not found: \n") + reply
484 # Pass to widget parser
485 if self.config[1] != "":
487 p = subprocess.Popen(smart_split_string(self.config[1], reply, sms_reply, reresult1, reresult2), stdout=subprocess.PIPE)
488 w_reply = p.communicate()[0].strip()
490 print >> log, _("Couldn't exec widget parser:")+str(e)
491 self.widget.error = 1
493 if self.config[4] != "":
495 w_reply = reresult1.group( self.config[14] )
497 self.widget.error = 1
498 w_reply = _("Group not found: \n") + reply
500 if self.config[2] != "":
502 p = subprocess.Popen(smart_split_string(self.config[2], reply, sms_reply, reresult1, reresult2))
504 print >> log, _("Couldn't exec chain:")+str(e)
505 self.widget.error = 1
507 banner = hildon.hildon_banner_show_information (self.widget, "", b_reply)
508 banner.set_timeout (5000)
510 self.widget.set_text(w_reply)
511 self.widget.processing = 0
513 def sms_timeout(self, version):
514 if version == self.sms_counter :
516 self.sms_ready = True
517 self.sms_signal.remove()
521 def timed_renew(self, version):
522 if version < self.timeout_version :
524 self.ussd_renew(self.widget, None)
527 def retry_renew(self,version):
528 if self.widget.error == 0 or self.widget.processing == 1 or version < self.retry_version :
530 self.ussd_renew(self.widget, None)
533 def reset_timed_renew (self) :
534 self.timeout_version += 1
535 if self.config[3] != 0 :
536 self.timer = gobject.timeout_add (60000*self.config[3], self.timed_renew, self.timeout_version)
538 class pHelpDialog(gtk.Dialog):
539 def __init__(self, heading, text):
540 gtk.Dialog.__init__(self, heading, None,
541 gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_NO_SEPARATOR,
542 (_("OK").encode("utf-8"), gtk.RESPONSE_OK))
543 label = gtk.Label(text)
544 label.set_line_wrap (True)
549 class UssdConfigDialog(gtk.Dialog):
550 def __init__(self, config, bg_color, text_color, id):
551 gtk.Dialog.__init__(self, _("USSD widget : "+id), None,
552 gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_NO_SEPARATOR,
553 (_("Save").encode("utf-8"), gtk.RESPONSE_OK))
555 self.font = config[8]
556 self.bg_color = bg_color
557 self.text_color = text_color
559 self.set_size_request(-1, 400)
560 self.ussdNumber = hildon.Entry(gtk.HILDON_SIZE_AUTO)
561 self.ussdNumber.set_text(config[0])
562 self.parser = hildon.Entry(gtk.HILDON_SIZE_AUTO)
563 self.parser.set_text(config[1])
564 self.b_parser = hildon.Entry(gtk.HILDON_SIZE_AUTO)
565 self.b_parser.set_text(config[12])
567 self.chain = hildon.Entry(gtk.HILDON_SIZE_AUTO)
568 self.chain.set_text(config[2])
569 self.update_interval = hildon.NumberEditor(0, 9999)
570 self.update_interval.set_value(config[3])
571 self.regexp = hildon.Entry(gtk.HILDON_SIZE_AUTO)
572 self.regexp.set_text(config[4])
573 self.widthEdit = hildon.NumberEditor(0, 1000)
574 self.widthEdit.set_value(config[5])
575 self.retryEdit = hildon.Entry(gtk.HILDON_SIZE_AUTO)
576 self.args = hildon.Entry(gtk.HILDON_SIZE_AUTO)
577 self.args.set_text(config[13])
578 self.reggroup = hildon.NumberEditor(0, 255)
579 self.reggroup.set_value(config[14])
581 selector = hildon.TouchSelector(text=True)
582 for i in ussd_languages_localized:
583 selector.append_text(i)
584 self.language = hildon.PickerButton(gtk.HILDON_SIZE_AUTO, hildon.BUTTON_ARRANGEMENT_HORIZONTAL)
585 self.language.set_selector(selector)
586 self.language.set_active(config[10])
587 self.language.set_title(_("USSD reply language"))
588 self.language.set_size_request(-1, -1)
590 self.wname = hildon.Entry(gtk.HILDON_SIZE_AUTO)
591 self.wname.set_text(config[9])
592 self.show_box = gtk.CheckButton(_("Enable banner. Parser:"))
593 self.show_box.connect("toggled", self.show_box_changed)
594 self.show_box.set_active(config[11])
601 self.retryEdit.set_text(text)
603 self.query_at_start = gtk.CheckButton(_("Execute query on start"))
604 self.query_at_start.set_active(config[6])
606 self.fontButton = gtk.Button(_("Font"))
607 self.fontButton.connect("clicked", self.on_show_font_selection)
609 self.colorButton = gtk.Button(_("Background color"))
610 self.colorButton.connect("clicked", self.on_show_color_selection)
611 self.textColorButton = gtk.Button(_("Text color"))
612 self.textColorButton.connect("clicked", self.on_show_text_color_selection)
614 phelp = gtk.Button("?")
615 phelp.connect("clicked", self.on_show_phelp)
617 bphelp = gtk.Button("?")
618 bphelp.connect("clicked", self.on_show_bphelp)
620 chelp = gtk.Button("?")
621 chelp.connect("clicked", self.on_show_chelp)
623 reghelp = gtk.Button("?")
624 reghelp.connect("clicked", self.on_show_reghelp)
626 retryhelp = gtk.Button("?")
627 retryhelp.connect("clicked", self.on_show_retryhelp)
629 numberhelp = gtk.Button("?")
630 numberhelp.connect("clicked", self.on_show_number_help)
632 area = hildon.PannableArea()
635 area.add_with_viewport(vbox)
637 numberBox = gtk.HBox()
638 numberLabel = gtk.Label(_("USSD number"))
639 numberLabel.set_alignment(0,0.6)
640 numberLabel.set_size_request(100, -1)
641 numberhelp.set_size_request(1, -1)
642 self.ussdNumber.set_size_request(200, -1)
643 numberBox.add(numberLabel)
644 numberBox.add(numberhelp)
645 numberBox.add(self.ussdNumber)
648 vbox.add(self.query_at_start)
651 nameLabel = gtk.Label(_("Name"))
652 nameLabel.set_alignment(0,0.6)
653 nameLabel.set_size_request(100, -1)
654 self.wname.set_size_request(200, -1)
655 nameBox.add(nameLabel)
656 nameBox.add(self.wname)
659 parserBox = gtk.HBox()
660 parserLabel = gtk.Label(_("Parser for widget"))
661 parserLabel.set_alignment(0,0.6)
662 parserLabel.set_size_request(200, -1)
663 phelp.set_size_request(10, -1)
664 parserBox.add(parserLabel)
667 vbox.add(self.parser)
669 b_parserBox = gtk.HBox()
670 self.show_box.set_size_request(200, -1)
671 bphelp.set_size_request(10, -1)
672 b_parserBox.add(self.show_box)
673 b_parserBox.add(bphelp)
674 vbox.add(b_parserBox)
675 vbox.add(self.b_parser)
677 chainBox = gtk.HBox()
678 chainLabel = gtk.Label(_("Chain"))
679 chainLabel.set_alignment(0,0.6)
680 chainLabel.set_size_request(200, -1)
681 chelp.set_size_request(10, -1)
682 chainBox.add(chainLabel)
687 regexpBox = gtk.HBox()
688 regexpLabel = gtk.Label(_("Regular expression"))
689 regexpLabel.set_alignment(0,0.6)
690 regexpLabel.set_size_request(200, -1)
691 regexpGroupLabel = gtk.Label(_("Group"))
692 regexpGroupLabel.set_size_request(1, -1)
693 reghelp.set_size_request(10, -1)
694 regexpBox.add(regexpLabel)
695 regexpBox.add(reghelp)
696 regexpBox.add(regexpGroupLabel)
698 self.reggroup.set_size_request(1,-1);
699 self.regexp.set_size_request(250,-1);
700 regexpInputBox = gtk.HBox()
701 regexpInputBox.add(self.regexp)
702 regexpInputBox.add(self.reggroup)
703 vbox.add(regexpInputBox)
705 widthBox = gtk.HBox()
706 widthLabel = gtk.Label(_("Max. width"))
707 widthLabel.set_alignment(0,0.6)
708 symbolsLabel = gtk.Label(_("symbols"))
709 widthLabel.set_size_request(140, -1)
710 self.widthEdit.set_size_request(50, -1)
711 symbolsLabel.set_size_request(40,-1)
712 widthBox.add(widthLabel)
713 widthBox.add(self.widthEdit)
714 widthBox.add(symbolsLabel)
717 updateBox = gtk.HBox()
718 updateLabel = gtk.Label(_("Update every"))
719 updateLabel.set_alignment(0,0.6)
720 minutesLabel = gtk.Label(_("minutes"))
721 updateLabel.set_size_request(140, -1)
722 self.update_interval.set_size_request(50, -1)
723 minutesLabel.set_size_request(40, -1)
724 updateBox.add(updateLabel)
725 updateBox.add(self.update_interval)
726 updateBox.add(minutesLabel)
729 retryBox = gtk.HBox()
730 retryLabel = gtk.Label(_("Retry pattern"))
731 retryLabel.set_alignment(0,0.6)
732 retryLabel.set_size_request(200, -1)
733 retryhelp.set_size_request(10, -1)
734 retryBox.add(retryLabel)
735 retryBox.add(retryhelp)
737 vbox.add(self.retryEdit)
739 argsLabel = gtk.Label(_("Additional ussdquery.py options"))
740 argsLabel.set_alignment(0,0.6)
745 viewBox.add(self.fontButton)
746 viewBox.add(self.textColorButton)
747 viewBox.add(self.colorButton)
750 self.sms_box = gtk.VBox()
751 self.sms_listener = gtk.CheckButton(_("Enable SMS listener."))
752 self.sms_listener.connect("toggled", self.sms_box_changed)
753 self.sms_listener.set_active(config[15])
754 vbox.add (self.sms_listener)
756 self.sms_number = hildon.Entry(gtk.HILDON_SIZE_AUTO)
757 self.sms_number.set_text(config[16])
758 smsNumberBox = gtk.HBox()
759 smsNumberLabel = gtk.Label(_("SMS number"))
760 smsNumberLabel.set_alignment(0,0.6)
761 smsNumberLabel.set_size_request(100, -1)
762 self.sms_number.set_size_request(200, -1)
763 smsNumberBox.add(smsNumberLabel)
764 smsNumberBox.add(self.sms_number)
765 self.sms_box.add(smsNumberBox)
767 smsRegexpLabel = gtk.Label(_("Regular expression"))
768 smsRegexpLabel.set_alignment(0,0.6)
769 self.sms_box.add(smsRegexpLabel)
771 self.sms_regexp = hildon.Entry(gtk.HILDON_SIZE_AUTO)
772 self.sms_regexp.set_text(config[17])
773 self.sms_box.add(self.sms_regexp)
775 self.sms_timeout = hildon.NumberEditor(0, 9999)
776 self.sms_timeout.set_value(config[18])
777 sms_timeout_box = gtk.HBox()
778 timeoutLabel = gtk.Label(_("Timeout"))
779 timeoutLabel.set_alignment(0,0.6)
780 secondsLabel = gtk.Label(_("seconds"))
781 timeoutLabel.set_size_request(140, -1)
782 self.sms_timeout.set_size_request(50, -1)
783 secondsLabel.set_size_request(40, -1)
784 sms_timeout_box.add(timeoutLabel)
785 sms_timeout_box.add(self.sms_timeout)
786 sms_timeout_box.add(secondsLabel)
787 self.sms_box.add(sms_timeout_box)
789 vbox.add(self.sms_box)
791 vbox.add(gtk.Label(_("DO NOT CHANGE. Unspecified is what you want.")))
792 vbox.add(self.language)
795 self.show_box_changed(None)
796 self.sms_box_changed(None)
799 #============ Dialog helper functions =============
800 def on_show_phelp(self, widget):
801 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."))
805 def on_show_bphelp(self, widget):
806 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."))
810 def on_show_chelp(self, widget):
811 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"))
815 def on_show_reghelp(self, widget):
816 dialog = pHelpDialog(_("Format help"), _("Standard python regexps. Use\n (.+?[\d\,\.]+)\n to delete everything after first number."))
820 def on_show_retryhelp(self, widget):
821 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\""))
825 def on_show_number_help(self, widget):
826 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."))
830 def on_error_regexp(self, error):
831 dialog = pHelpDialog(_("Regexp syntax error"), error )
835 def on_error_ussd_number(self):
836 dialog = pHelpDialog(_("Incorrect USSD number"), _("USSD number should contain only digits, +, * or #") )
840 def on_error_retry_pattern(self):
841 dialog = pHelpDialog(_("Incorrect retry pattern"), _("Retry pattern should contain only numbers, delimited by -") )
845 def on_show_color_selection (self, event):
846 colorDialog = gtk.ColorSelectionDialog(_("Choose background color"))
847 colorDialog.colorsel.set_current_color(self.bg_color)
848 if colorDialog.run() == gtk.RESPONSE_OK :
849 self.bg_color = colorDialog.colorsel.get_current_color()
850 colorDialog.destroy()
852 def on_show_text_color_selection (self, event):
853 colorDialog = gtk.ColorSelectionDialog(_("Choose text color"))
854 colorDialog.colorsel.set_current_color(self.text_color)
855 if colorDialog.run() == gtk.RESPONSE_OK :
856 self.text_color = colorDialog.colorsel.get_current_color()
857 colorDialog.destroy()
859 def on_show_font_selection (self, event):
860 fontDialog = gtk.FontSelectionDialog(_("Choose a font"))
861 fontDialog.set_font_name(self.font.to_string())
863 if fontDialog.run() != gtk.RESPONSE_OK :
867 self.font = pango.FontDescription (fontDialog.get_font_name())
870 def show_box_changed (self, event):
871 if self.show_box.get_active():
876 def sms_box_changed (self, event):
877 if self.sms_listener.get_active():
882 def smart_split_string (str, reply1, reply2, reres1 = None, reres2 = None) :
885 # Is simbol backslashed?
887 # Quotes: 1 - ", 2 - ', 0 - no quotes
891 # Current substitution simbol
894 for i in range(len(str)) :
896 if str[i] in ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"] :
902 if reres2 != None and num != 0:
903 word += reres2.group(num)
907 if reres1 != None and num != 0:
908 word += reres1.group(num)
914 # Delete backslash if it delimites usual numbers from % or &
915 if str[i] == '\\' and i < len(str)-1 and str[i+1] in ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"] :
917 if bs == 0 and (str[i] == '"' and qs == 1 or str[i] == "'" and qs == 2) :
919 elif bs == 0 and qs == 0 and (str[i] == '"' or str[i] == "'") :
924 elif bs == 0 and str[i] == '\\' :
926 elif bs == 0 and (str[i] == '%' or str[i] == '&') :
930 if bs == 1 and str[i] != '\\' and str[i] != '"' and str[i] != "'" :
932 if qs == 0 and (str[i] == " " or str[i] == "\t") :
941 if reres2 != None and num != 0 and num != -1:
942 word += reres2.group(num)
946 if reres1 != None and num != 0 and num != -1:
947 word += reres1.group(num)
954 def check_regexp(regexp):
961 def check_number(number):
963 if not (s in ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "+", "*", "#", " "]) :
967 #=============== The widget itself ================
969 def get_color(logicalcolorname):
970 settings = gtk.settings_get_default()
971 color_style = gtk.rc_get_style_by_paths(settings, 'GtkButton', 'osso-logical-colors', gtk.Button)
972 return color_style.lookup_color(logicalcolorname)
974 class UssdWidgetPlugin(hildondesktop.HomePluginItem):
976 hildondesktop.HomePluginItem.__init__(self)
979 self.bg_color=gtk.gdk.color_parse('#000000')
980 self.text_color=gtk.gdk.color_parse('#ffffff')
982 self.timeout_version = 0
984 colormap = self.get_screen().get_rgba_colormap()
985 self.set_colormap (colormap)
987 self.controller = USSD_Controller(self)
989 # TODO Click event would be better
990 self.connect("button-press-event", self.controller.ussd_renew)
992 self.vbox = gtk.HBox()
995 self.set_settings(True)
996 self.connect("show-settings", self.controller.on_show_settings)
997 self.label = gtk.Label("")
999 self.vbox.add(self.label)
1000 self.vbox.set_child_packing(self.label, False, False, 0, gtk.PACK_START)
1001 self.label.set_padding(15, 10)
1002 self.label.set_size_request(-1,-1)
1003 self.set_size_request(-1,-1)
1004 self.label.set_line_wrap (True)
1006 self.vbox.show_all()
1008 DBusGMainLoop(set_as_default=True)
1009 bus = dbus.SystemBus()
1010 signal = bus.add_signal_receiver(self.set_bg_color_text, path='/su/kibergus/ussd_widget', dbus_interface='su.kibergus.ussd_widget', signal_name='set_bg_color')
1011 signal = bus.add_signal_receiver(self.set_text_color_text, path='/su/kibergus/ussd_widget', dbus_interface='su.kibergus.ussd_widget', signal_name='set_text_color')
1012 signal = bus.add_signal_receiver(self.ussd_renew, path='/su/kibergus/ussd_widget', dbus_interface='su.kibergus.ussd_widget', signal_name='renew')
1015 config = self.controller.read_config(self.get_applet_id())
1016 self.set_width(config[5])
1017 self.set_text(config[9])
1019 self.controller.ussd_renew(self, None)
1021 self.label.modify_font(config[8])
1022 self.controller.reset_timed_renew()
1023 hildondesktop.HomePluginItem.do_show(self)
1025 def error_return (self):
1026 if self.error == 1 and self.processing == 0:
1027 self.set_text(self.text)
1031 # -1 - This is a permanent text message
1032 # 0 - This is service message, but it shouldn't be hidden automatically
1033 # >0 - This is service message, show permament message after showfor milliseconds
1034 def set_text(self, text, showfor=-1):
1036 # Show previous text after 5 seconds
1037 gobject.timeout_add (showfor, self.error_return)
1042 config = self.controller.get_config()
1043 self.label.set_text(text)
1048 def set_width(self, width):
1050 self.label.set_width_chars (width)
1052 self.label.set_width_chars(-1)
1054 def ussd_renew(self, id):
1055 if id == self.get_applet_id():
1056 self.controller.ussd_renew(self, None)
1058 def set_bg_color_text(self, id, color):
1059 if id == self.get_applet_id():
1061 self.set_bg_color(gtk.gdk.color_parse(color.strip()))
1063 print >> log, _("Unable to parse colour specification")
1066 def set_text_color_text(self, id, color):
1067 if id == self.get_applet_id():
1069 self.set_text_color(gtk.gdk.color_parse(color.strip()))
1071 print >> log, _("Unable to parse colour specification")
1074 def set_bg_color(self, color):
1075 self.bg_color = color
1077 def get_bg_color(self):
1078 return self.bg_color
1080 def set_text_color(self, color):
1081 self.label.modify_fg(gtk.STATE_NORMAL, color)
1082 self.text_color = color
1084 def get_text_color(self):
1085 return self.text_color
1087 def _expose(self, event):
1088 cr = self.window.cairo_create()
1091 width, height = self.label.allocation[2], self.label.allocation[3]
1093 #/* a custom shape, that could be wrapped in a function */
1094 x0 = 0 #/*< parameters like cairo_rectangle */
1097 radius = min(15, width/2, height/2) #/*< and an approximate curvature radius */
1102 cr.move_to (x0, y0 + radius)
1103 cr.arc (x0 + radius, y0 + radius, radius, 3.14, 1.5 * 3.14)
1104 cr.line_to (x1 - radius, y0)
1105 cr.arc (x1 - radius, y0 + radius, radius, 1.5 * 3.14, 0.0)
1106 cr.line_to (x1 , y1 - radius)
1107 cr.arc (x1 - radius, y1 - radius, radius, 0.0, 0.5 * 3.14)
1108 cr.line_to (x0 + radius, y1)
1109 cr.arc (x0 + radius, y1 - radius, radius, 0.5 * 3.14, 3.14)
1113 fg_color = get_color("ActiveTextColor")
1115 if self.processing :
1118 bg_color=self.bg_color
1120 cr.set_source_rgba (bg_color.red / 65535.0, bg_color.green/65535.0, bg_color.blue/65535.0, 0.7)
1124 cr.set_source_rgba (1.0, 0.0, 0.0, 0.5)
1126 cr.set_source_rgba (fg_color.red / 65535.0, fg_color.green / 65535.0, fg_color.blue / 65535.0, 0.7)
1129 def do_expose_event(self, event):
1131 self._expose (event)
1132 self.vbox.do_expose_event (self, event)
1134 hd_plugin_type = UssdWidgetPlugin
1136 # The code below is just for testing purposes.
1137 # It allows to run the widget as a standalone process.
1138 if __name__ == "__main__":
1139 plugin_id = "ussd-widget.console"
1140 if len(sys.argv) == 2:
1142 plugin_id = "ussd-widget.desktop-"+str(int(sys.argv[1]))
1144 print >> log, "Plugin id must be integer"
1148 gobject.type_register(hd_plugin_type)
1149 obj = gobject.new(hd_plugin_type, plugin_id=plugin_id)