~ubuntu-branches/ubuntu/utopic/monajat/utopic

« back to all changes in this revision

Viewing changes to monajat/applet.py

  • Committer: Package Import Robot
  • Author(s): أحمد المحمودي (Ahmed El-Mahmoudy)
  • Date: 2014-04-12 19:05:04 UTC
  • mfrom: (1.1.6)
  • Revision ID: package-import@ubuntu.com-20140412190504-ktaasa5xwpwbv6ob
Tags: 2.6.5-1
* New upstream release.
* Update URL in watch file.
* debian/control:
  + Replaced python-gtk2 & python-notify with python-gobject in
    monajat-applet's Depends
  + Update Standards-Version to 3.9.5
  + Add intltool to Build-Deps
* debian/copyright: Update copyright years.
* debian/gbp.conf: Remove compression field.
* Dropped fix_locale.diff patch, as it is included upstream.
* debian/rules: Replace overrides for dh_auto_build & dh_auto_clean with
  overrides for dh_auto_{clean,build,install} to run them with
  python_distutils & makefile build systems.
* Added separate_pyinstall.diff patch to separate python build/install calls
  from Makefile

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
# -*- coding: utf-8 -*-
 
2
# -*- Mode: Python; py-indent-offset: 4 -*-
2
3
import os, os.path
3
4
import itl
4
5
from monajat import Monajat
6
7
import locale, gettext
7
8
import re
8
9
 
9
 
import glib
10
 
import gtk
11
 
import pynotify
 
10
from gi.repository import Gtk, Gdk, Notify, Gst, GObject
12
11
import cgi
13
12
import math
14
13
import json
15
14
import time
16
 
import gst
17
15
 
18
16
# in gnome3 ['actions', 'action-icons', 'body', 'body-markup', 'icon-static', 'persistence']
19
17
# in gnome2 ['actions', 'body', 'body-hyperlinks', 'body-markup', 'icon-static', 'sound']
20
18
# "resident"
21
19
 
22
 
class SoundPlayer:
23
 
  def __init__(self, fn=None, change_play_status=None):
24
 
    if not fn: fn=""
25
 
    self.fn=fn
26
 
    self.change_play_status=change_play_status
27
 
    self.gst_player = gst.element_factory_make("playbin2", "player")
28
 
    self.gst_player.set_property("uri", "file://" + fn)
29
 
    bus = self.gst_player.get_bus()
30
 
    bus.add_signal_watch()
31
 
    bus.connect("message", self.on_message)
32
 
    
33
 
  def play(self):
34
 
    fn=self.fn
35
 
    if fn and os.path.isfile(fn):
36
 
      self.gst_player.set_state(gst.STATE_PLAYING)
37
 
  
38
 
  def stop(self):
39
 
    self.gst_player.set_state(gst.STATE_NULL)
40
 
  
41
 
  def set_filename(self,fn):
42
 
    if not fn: fn=""
43
 
    self.fn=fn
44
 
    self.gst_player.set_property("uri", "file://" + fn)
45
 
    
46
 
  def on_message(self, bus, message):
47
 
    t = message.type
48
 
    # print t
49
 
    if t == gst.MESSAGE_EOS:
50
 
      if self.change_play_status: self.change_play_status()
51
 
      self.gst_player.set_state(gst.STATE_NULL)
52
 
    elif t == gst.MESSAGE_ERROR:
53
 
      self.gst_player.set_state(gst.STATE_NULL)
54
 
      err, debug = message.parse_error()
55
 
      print "Error: %s" % err, debug
 
20
class SoundPlayer(object):
 
21
    def __init__(self, fn = "", change_play_status = None):
 
22
        """ SoundPlayer Using gst """
 
23
        self.fn = fn
 
24
        self.change_play_status = change_play_status
 
25
        Gst.init_check(None)
 
26
        self.gst_player = Gst.ElementFactory.make("playbin", "Ojuba-SoundPlayer")
 
27
        self.gst_player.set_property("uri", "file://" + fn)
 
28
        bus = self.gst_player.get_bus()
 
29
        bus.add_signal_watch()
 
30
        #bus.connect("message", self.on_message)
 
31
        bus.connect('message::eos', self.on_message)
 
32
        bus.connect('message::error', self.on_message)
 
33
        
 
34
    def play(self):
 
35
        fn = self.fn
 
36
        if fn and os.path.isfile(fn):
 
37
            self.gst_player.set_state(Gst.State.PLAYING)
 
38
    
 
39
    def stop(self):
 
40
        self.gst_player.set_state(Gst.State.NULL)
 
41
    
 
42
    def set_filename(self,fn):
 
43
        if not fn or not os.path.isfile(fn):
 
44
            return
 
45
        self.fn = fn
 
46
        self.gst_player.set_property("uri", "file://" + fn)
 
47
        
 
48
    def on_message(self, bus, message):
 
49
        if message:
 
50
            print message
 
51
        self.gst_player.set_state(Gst.State.NULL)
56
52
 
57
 
normalize_tb={
 
53
normalize_tb = {
58
54
65: 97, 66: 98, 67: 99, 68: 100, 69: 101, 70: 102, 71: 103, 72: 104, 73: 105, 74: 106, 75: 107, 76: 108, 77: 109, 78: 110, 79: 111, 80: 112, 81: 113, 82: 114, 83: 115, 84: 116, 85: 117, 86: 118, 87: 119, 88: 120, 89: 121, 90: 122,
59
55
1600: None, 1569: 1575, 1570: 1575, 1571: 1575, 1572: 1575, 1573: 1575, 1574: 1575,
60
 
1577: 1607, # teh marboota ->  haa
 
56
1577: 1607, # teh marboota ->    haa
61
57
1611: None, 1612: None, 1613: None, 1614: None, 1615: None, 1616: None, 1617: None, 1618: None, 1609: 1575}
62
58
 
63
 
 
64
 
class ConfigDlg(gtk.Dialog):
65
 
  def __init__(self, applet):
66
 
    gtk.Dialog.__init__(self)
67
 
    self.applet=applet
68
 
    self.m=applet.m
69
 
    self.set_size_request(300,400)
70
 
    self.set_resizable(False) # FIXME: reconsider this
71
 
    self.connect('delete-event', lambda w,*a: w.hide() or True)
72
 
    self.connect('response', lambda w,*a: w.hide() or True)
73
 
    self.set_title(_('Monajat Configuration'))
74
 
    self.add_button(_('Cancel'), gtk.RESPONSE_CANCEL)
75
 
    self.add_button(_('Save'), gtk.RESPONSE_OK)
76
 
    tabs=gtk.Notebook()
77
 
    self.get_content_area().add(tabs)
78
 
    vb=gtk.VBox()
79
 
    tabs.append_page(vb, gtk.Label(_('Generic')))
80
 
    self.auto_start = b = gtk.CheckButton(_("Auto start"))
81
 
    self.auto_start.set_active(not os.path.exists(self.applet.skip_auto_fn))
82
 
    vb.pack_start(b, False, False, 2)
83
 
    self.show_merits = b = gtk.CheckButton(_("Show merits"))
84
 
    self.show_merits.set_active(self.applet.conf['show_merits'])
85
 
    vb.pack_start(b, False, False, 2)
86
 
    hb = gtk.HBox()
87
 
    vb.pack_start(hb, False, False, 2)
88
 
    hb.pack_start(gtk.Label(_('Language:')), False, False, 2)
89
 
    self.lang = b = gtk.combo_box_new_text()
90
 
    hb.pack_start(b, False, False, 2)
91
 
    selected = 0
92
 
    for i,j in enumerate(self.applet.m.langs):
93
 
      b.append_text(j)
94
 
      if self.m.lang==j: selected=i
95
 
    b.set_active(selected)
96
 
    
97
 
    hb = gtk.HBox()
98
 
    vb.pack_start(hb, False, False, 2)
99
 
    hb.pack_start(gtk.Label(_('Time:')), False, False, 2)
100
 
    hb.pack_start(gtk.HBox(), True, True, 2)
101
 
    self.timeout = b = gtk.SpinButton(gtk.Adjustment(5, 0, 90, 5, 5))
102
 
    b.set_value(self.applet.conf['minutes'])
103
 
    hb.pack_start(b, False, False, 2)
104
 
    hb.pack_start(gtk.Label(_('minutes')), False, False, 2)
105
 
 
106
 
    vb=gtk.VBox()
107
 
    tabs.append_page(vb, gtk.Label(_('Location')))
108
 
    
109
 
    hb=gtk.HBox()
110
 
    vb.pack_start(hb, False, False, 2)
111
 
    e=gtk.Entry()
112
 
    e.connect('activate', self._city_search_cb)
113
 
    hb.pack_start(e, False, False, 2)
114
 
    
115
 
    s = gtk.TreeStore(str, bool, int, str) # label, is_city, id, normalized label
116
 
    self.cities_tree=tree=gtk.TreeView(s)
117
 
    col=gtk.TreeViewColumn('Location', gtk.CellRendererText(), text=0)
118
 
    col.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE)
119
 
    tree.insert_column(col, -1)
120
 
    tree.set_enable_search(True)
121
 
    tree.set_search_column(3)
122
 
    tree.set_headers_visible(False)
123
 
    tree.set_tooltip_column(0)
124
 
    
125
 
    scroll=gtk.ScrolledWindow()
126
 
    scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS)
127
 
    scroll.add(tree)
128
 
    vb.pack_start(scroll, True, True, 2)
129
 
 
130
 
    self.sound_player = SoundPlayer(self.applet.conf['athan_media_file'], self.change_play_status)
131
 
    vb=gtk.VBox()
132
 
    tabs.append_page(vb, gtk.Label(_('Notification')))
133
 
    hb=gtk.HBox()
134
 
    vb.pack_start(hb, False, True, 3)
135
 
    hb.pack_start(gtk.Label(_('Sound:')), False, True, 2)
136
 
    self.sound_file = b = gtk.FileChooserButton(_('Choose Athan media file'))
137
 
    self.sound_file.set_filename(self.applet.conf['athan_media_file'])
138
 
    ff=gtk.FileFilter()
139
 
    ff.set_name(_('Sound Files'))
140
 
    ff.add_pattern('*.ogg')
141
 
    ff.add_pattern('*.mp3')
142
 
    b.add_filter(ff)
143
 
    ff=gtk.FileFilter()
144
 
    ff.set_name(_('All files'))
145
 
    ff.add_pattern('*')
146
 
    b.add_filter(ff)
147
 
    self.play_b=pb=gtk.Button(_('Play'))
148
 
    pb.connect('clicked', self.play_cb)
149
 
    hb.pack_end(pb, False, False, 2)
150
 
    hb.pack_end(b, True, True, 5)
151
 
    
152
 
    hb = gtk.HBox()
153
 
    vb.pack_start(hb, False, False, 3)
154
 
    self.notify_before_b = b = gtk.CheckButton(_("Notify before"))
155
 
    self.notify_before_t = t = gtk.SpinButton(gtk.Adjustment(5, 5, 20, 5, 5))
156
 
    b.set_active(self.applet.conf['notify_before_min']!=0)
157
 
    t.set_value(self.applet.conf['notify_before_min'])
158
 
    hb.pack_start(b,True,True,2)
159
 
    hb.pack_start(t,False,False,2)
160
 
    hb.pack_start(gtk.Label(_('minutes')),False,False,2)
161
 
    
162
 
    self._fill_cities()
163
 
 
164
 
  def change_play_status(self, status=None):
165
 
    if status==None: status=self.sound_player.gst_player.get_state()
166
 
    if status==gst.STATE_PLAYING:
167
 
      self.sound_file.set_sensitive(False)
168
 
      self.play_b.set_property('label', _('Stop'))
169
 
    else:
170
 
      self.sound_file.set_sensitive(True)
171
 
      self.play_b.set_property('label', _('Play'))
172
 
 
173
 
  def play_cb(self, b):
174
 
    fn=self.sound_file.get_filename()
175
 
    if not fn: fn=''
176
 
    if b.get_label() == _('Play'):
177
 
      if not os.path.isfile(fn): return
178
 
      self.change_play_status(gst.STATE_PLAYING)
179
 
      self.sound_player.set_filename(fn)
180
 
      self.sound_player.play()
181
 
    else:
182
 
      self.change_play_status(gst.STATE_NULL)
183
 
      self.sound_player.stop()
184
 
 
185
 
  def _city_search_cb(self, e):
186
 
    # FIXME: if same as last successful search text don't update first_match_path
187
 
    # FIXME: and if self.city_found same as first_match_path highlight in red
188
 
    txt=e.get_text().strip().lower()
189
 
    if type(txt)==unicode: txt=txt.translate(normalize_tb)
190
 
    else: txt=txt.decode('utf-8').translate(normalize_tb)
191
 
    e.modify_base(gtk.STATE_NORMAL, None)
192
 
    tree=self.cities_tree
193
 
    store, p=tree.get_selection().get_selected_rows()
194
 
    if p: current=p[0]
195
 
    else: current=None
196
 
    limit=None
197
 
    def tree_walk_cb(model, path, i):
198
 
      if current and path<=current: return False
199
 
      if limit and path>=limit: return True
200
 
      if txt in store.get_value(i,3):
201
 
        tree.expand_to_path(path)
202
 
        tree.scroll_to_cell(path)
203
 
        tree.get_selection().select_iter(i)
204
 
        self.city_found=path
205
 
        return True
206
 
    self.city_found=None
207
 
    store.foreach(tree_walk_cb)
208
 
    if not self.city_found:
209
 
      limit=current
210
 
      current=None
211
 
      store.foreach(tree_walk_cb)
212
 
      if not self.city_found:
213
 
        e.modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse("#FFCCCC"))
214
 
    
215
 
  def _fill_cities(self):
216
 
    tree=self.cities_tree
217
 
    s=tree.get_model()
218
 
    rows=self.m.cities_c.execute('SELECT * FROM cities')
219
 
    country, country_i = None, None
220
 
    state, state_i=None, None
221
 
    city_path=None
222
 
    for R in rows:
223
 
      r=dict(R)
224
 
      if country!=r['country']:
225
 
        country_i=s.append(None,(r['country'], False, 0, r['country'].lower().translate(normalize_tb)))
226
 
      if state!=r['state']:
227
 
        state_i=s.append(country_i,(r['state'], False, 0, r['state'].lower().translate(normalize_tb)))
228
 
      country=r['country']
229
 
      state=r['state']
230
 
      if r['locale_name']: city=u'%s - %s' % (r['name'], r['locale_name'])
231
 
      else: city=r['name']
232
 
      city_i=s.append(state_i,(city, True, r['id'], city.lower().translate(normalize_tb)))
233
 
      if self.applet.conf.get('city_id',None)==r['id']: city_path=s.get_path(city_i)
234
 
    if city_path:
235
 
      tree.expand_to_path(city_path)
236
 
      tree.get_selection().select_path(city_path)
237
 
      tree.scroll_to_cell(city_path)
238
 
 
239
 
  def run(self, *a, **kw):
240
 
    self.auto_start.set_active(not os.path.exists(self.applet.skip_auto_fn))
241
 
    return gtk.Dialog.run(self, *a, **kw)
 
59
def to_uincode(Str):
 
60
    try:
 
61
        Str = str(Str)
 
62
    except UnicodeEncodeError:
 
63
        pass
 
64
    if type(Str) == unicode:
 
65
        return Str.translate(normalize_tb)
 
66
    else:
 
67
        return Str.decode('utf-8').translate(normalize_tb)
 
68
        
 
69
class ConfigDlg(Gtk.Dialog):
 
70
    def __init__(self, applet):
 
71
        Gtk.Dialog.__init__(self)
 
72
        self.applet = applet
 
73
        self.m = applet.m
 
74
        self.cities_ls = self.get_cities()
 
75
        #self.set_size_request(300,400)
 
76
        self.set_resizable(False) # FIXME: reconsider this
 
77
        self.connect('delete-event', lambda w,*a: w.hide() or True)
 
78
        self.connect('response', lambda w,*a: w.hide() or True)
 
79
        self.set_title(_('Monajat Configuration'))
 
80
        self.add_button(_('Cancel'), Gtk.ResponseType.CANCEL)
 
81
        self.add_button(_('Save'), Gtk.ResponseType.OK)
 
82
        tabs = Gtk.Notebook()
 
83
        tabs.set_size_request(-1,350)
 
84
        self.get_content_area().add(tabs)
 
85
        vb = Gtk.VBox(False, 2)
 
86
        tabs.append_page(vb, Gtk.Label(_('Generic')))
 
87
        self.auto_start = b = Gtk.CheckButton(_("Auto start"))
 
88
        self.auto_start.set_active(not os.path.exists(self.applet.skip_auto_fn))
 
89
        vb.pack_start(b, False, False, 2)
 
90
        self.show_merits = b = Gtk.CheckButton(_("Show merits"))
 
91
        self.show_merits.set_active(self.applet.conf['show_merits'])
 
92
        vb.pack_start(b, False, False, 2)
 
93
        hb = Gtk.HBox()
 
94
        vb.pack_start(hb, False, False, 2)
 
95
        hb.pack_start(Gtk.Label(_('Language:')), False, False, 2)
 
96
        self.lang = b = Gtk.ComboBoxText.new()
 
97
        hb.pack_end(b, False, False, 2)
 
98
        selected = 0
 
99
        for i,j in enumerate(self.applet.m.langs):
 
100
            b.append_text(j)
 
101
            if self.m.lang == j:
 
102
                selected=i
 
103
        b.set_active(selected)
 
104
        
 
105
        hb = Gtk.HBox()
 
106
        vb.pack_start(hb, False, False, 2)
 
107
        hb.pack_start(Gtk.Label(_('Time:')), False, False, 2)
 
108
        hb.pack_start(Gtk.HBox(), True, True, 2)
 
109
        adj = Gtk.Adjustment(5, 0, 90, 5, 5)
 
110
        self.timeout = b = Gtk.SpinButton()
 
111
        b.set_adjustment(adj)
 
112
        b.set_value(self.applet.conf['minutes'])
 
113
        hb.pack_start(b, False, False, 2)
 
114
        hb.pack_start(Gtk.Label(_('minutes')), False, False, 2)
 
115
 
 
116
        vb = Gtk.VBox(False, 2)
 
117
        tabs.append_page(vb, Gtk.Label(_('Location')))
 
118
        
 
119
        hb = Gtk.HBox()
 
120
        hb.pack_start(Gtk.Label(_('Current city:')), False, False, 2)
 
121
        vb.pack_start(hb, False, False, 6)
 
122
        hb = Gtk.HBox()
 
123
        vb.pack_start(hb, False, False, 2)
 
124
        # read cuurent city full name
 
125
        c = self.get_city('id', self.applet.conf['city_id'])
 
126
        if c:
 
127
            c = c[0]
 
128
            self.user_city = c['name']
 
129
        else:
 
130
            c = {}
 
131
            c['country'] = _('Please, Secify your city')
 
132
            c['state'] = ''
 
133
            c['name'] = ''
 
134
            c['locale_name'] = ''
 
135
            self.user_city = 'Makka'
 
136
        # set locale_name='' instead of None
 
137
        c['locale_name'] = c['locale_name'] or ''
 
138
        cl = '%(country)s - %(state)s - %(name)s %(locale_name)s' % c
 
139
        self.cur_city_l = l = Gtk.Entry()
 
140
        l.set_editable(False)
 
141
        l.set_text(cl)
 
142
        hb.pack_start(l, True, True, 2)
 
143
        
 
144
        hb = Gtk.HBox()
 
145
        hb.pack_start(Gtk.Label(_('Change city:')), False, False, 2)
 
146
        vb.pack_start(hb, False, False, 8)
 
147
        self.Search_e = e = Gtk.Entry()
 
148
        e.connect('activate', self._city_search_cb)
 
149
        hb.pack_start(e, True, True, 2)
 
150
        
 
151
        s = Gtk.TreeStore(str, bool, int, str) # label, is_city, id, normalized label
 
152
        self.cities_tree = tree = Gtk.TreeView(s)
 
153
        col = Gtk.TreeViewColumn('Location', Gtk.CellRendererText(), text=0)
 
154
        col.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
 
155
        tree.insert_column(col, -1)
 
156
        tree.set_enable_search(True)
 
157
        tree.set_search_column(3)
 
158
        tree.set_headers_visible(False)
 
159
        tree.set_tooltip_column(0)
 
160
        
 
161
        scroll = Gtk.ScrolledWindow()
 
162
        scroll.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.ALWAYS)
 
163
        scroll.add(tree)
 
164
        vb.pack_start(scroll, True, True, 2)
 
165
        self.sound_player = SoundPlayer(self.applet.conf['athan_media_file'],
 
166
                                        self.change_play_status)
 
167
        vb = Gtk.VBox(False, 2)
 
168
        tabs.append_page(vb, Gtk.Label(_('Notification')))
 
169
        hb = Gtk.HBox()
 
170
        vb.pack_start(hb, False, True, 3)
 
171
        hb.pack_start(Gtk.Label(_('Sound:')), False, True, 2)
 
172
        self.sound_file = b = Gtk.FileChooserButton(_('Choose Athan media file'))
 
173
        
 
174
        if os.path.isfile(self.applet.conf['athan_media_file']):
 
175
            self.sound_file.set_filename(self.applet.conf['athan_media_file'])
 
176
        ff = Gtk.FileFilter()
 
177
        ff.set_name(_('Sound Files'))
 
178
        ff.add_pattern('*.ogg')
 
179
        ff.add_pattern('*.mp3')
 
180
        b.add_filter(ff)
 
181
        ff = Gtk.FileFilter()
 
182
        ff.set_name(_('All files'))
 
183
        ff.add_pattern('*')
 
184
        b.add_filter(ff)
 
185
        self.play_b = pb = Gtk.Button(_('Play'))
 
186
        pb.connect('clicked', self.play_cb)
 
187
        hb.pack_end(pb, False, False, 2)
 
188
        hb.pack_end(b, True, True, 5)
 
189
        
 
190
        hb = Gtk.HBox()
 
191
        vb.pack_start(hb, False, False, 3)
 
192
        self.notify_before_b = b = Gtk.CheckButton(_("Notify before"))
 
193
        adj = Gtk.Adjustment(5, 5, 20, 5, 5)
 
194
        self.notify_before_t = t = Gtk.SpinButton()
 
195
        t.set_adjustment(adj)
 
196
        b.set_active(self.applet.conf['notify_before_min'] != 0)
 
197
        t.set_value(self.applet.conf['notify_before_min'])
 
198
        hb.pack_start(b,True,True,2)
 
199
        hb.pack_start(t,False,False,2)
 
200
        hb.pack_start(Gtk.Label(_('minutes')),False,False,2)
 
201
        self._city_search_cb(self.Search_e, self.user_city)
 
202
        #self._fill_cities()
 
203
 
 
204
    def change_play_status(self, status = None):
 
205
        if status == None:
 
206
            status = self.sound_player.gst_player.get_state()
 
207
        if status == Gst.State.PLAYING:
 
208
            self.sound_file.set_sensitive(False)
 
209
            self.play_b.set_property('label', _('Stop'))
 
210
        else:
 
211
            self.sound_file.set_sensitive(True)
 
212
            self.play_b.set_property('label', _('Play'))
 
213
 
 
214
    def play_cb(self, b):
 
215
        fn = self.sound_file.get_filename()
 
216
        if not fn:
 
217
            fn = ''
 
218
        if b.get_label() == _('Play'):
 
219
            if not os.path.isfile(fn):
 
220
                return
 
221
            self.change_play_status(Gst.State.PLAYING)
 
222
            self.sound_player.set_filename(fn)
 
223
            self.sound_player.play()
 
224
        else:
 
225
            self.change_play_status(Gst.State.NULL)
 
226
            self.sound_player.stop()
 
227
 
 
228
    def get_cities(self):
 
229
        rows = self.m.cities_c.execute('SELECT * FROM cities')
 
230
        rows = map(lambda a: dict(a), rows.fetchall())
 
231
        return rows
 
232
        
 
233
    def get_city(self, f='id', v=''):
 
234
        '''
 
235
            filter self.cities_ls
 
236
        '''
 
237
        return filter(lambda a: a[f] == v, self.cities_ls)
 
238
        
 
239
    def _city_search_cb(self, e, v = None):
 
240
        e.modify_fg(Gtk.StateType.NORMAL, None)
 
241
        if v:
 
242
            txt = v
 
243
        else:
 
244
            txt = e.get_text().strip().lower()
 
245
        txt = to_uincode(txt)
 
246
        if self.user_city == txt:
 
247
            self._search_cb(txt)
 
248
            return
 
249
        else:
 
250
            self.user_city = txt
 
251
        rows = filter(lambda a: txt in str(a['name']).lower() or \
 
252
                                txt in to_uincode(a['locale_name']) or \
 
253
                                txt in str(a['state']).lower() or \
 
254
                                txt in str(a['country']).lower(), self.cities_ls)
 
255
       
 
256
        country, country_i = None, None
 
257
        state, state_i = None, None
 
258
        city_path = None
 
259
        s = self.cities_tree.get_model()
 
260
        s.clear()
 
261
        for r in rows:
 
262
            #r = dict(R)
 
263
            if country != r['country']:
 
264
                country_i = s.append(None,
 
265
                                     (r['country'],
 
266
                                     False,
 
267
                                     0,
 
268
                                     r['country'].lower().translate(normalize_tb)))
 
269
            if state != r['state']:
 
270
                state_i = s.append(country_i,(r['state'],
 
271
                                   False,
 
272
                                   0,
 
273
                                   r['state'].lower().translate(normalize_tb)))
 
274
            country = r['country']
 
275
            state = r['state']
 
276
            if r['locale_name']:
 
277
                city = u'%s - %s' % (r['name'], r['locale_name'])
 
278
            else:
 
279
                city = r['name']
 
280
            city_i = s.append(state_i,(city,
 
281
                                      True,
 
282
                                      r['id'],
 
283
                                      city.lower().translate(normalize_tb)))
 
284
        self._search_cb(txt)
 
285
    
 
286
    def _search_cb(self, txt):
 
287
        # FIXME: if same as last successful search text don't update first_match_path
 
288
        # FIXME: and if self.city_found same as first_match_path highlight in red
 
289
        # we can use self.search_last_path to fix search
 
290
        if type(txt) == unicode:
 
291
            txt = txt.encode('utf-8')
 
292
        e = self.Search_e
 
293
        e.modify_fg(Gtk.StateType.NORMAL, None)
 
294
        tree = self.cities_tree
 
295
        store, p = tree.get_selection().get_selected_rows()
 
296
        if p:
 
297
            current = p[0]
 
298
        else:
 
299
            current = None
 
300
        limit = None
 
301
        def tree_walk_cb(model, path, i, *a):
 
302
            if current and path <= current:
 
303
                return False
 
304
            if limit and path >= limit:
 
305
                return True
 
306
            f = txt in store.get_value(i, 3)
 
307
            if f:
 
308
                tree.expand_to_path(path)
 
309
                tree.scroll_to_cell(path)
 
310
                tree.get_selection().select_iter(i)
 
311
                self.city_found = path
 
312
                return True
 
313
        self.city_found = None
 
314
        store.foreach(tree_walk_cb, tree)
 
315
        if not self.city_found:
 
316
            limit = current
 
317
            current = None
 
318
            store.foreach(tree_walk_cb, tree)
 
319
            if not self.city_found:
 
320
                e.modify_fg(Gtk.StateType.NORMAL, Gdk.color_parse("#FF0000"))
 
321
                
 
322
    def _fill_cities(self):
 
323
        # FIXME: rerwite one fuction for fill cities and search, to reduce time
 
324
        tree = self.cities_tree
 
325
        s = tree.get_model()
 
326
        rows = self.m.cities_c.execute('SELECT * FROM cities')
 
327
        country, country_i = None, None
 
328
        state, state_i = None, None
 
329
        city_path = None
 
330
        for R in rows:
 
331
            r = dict(R)
 
332
            if country != r['country']:
 
333
                country_i = s.append(None,
 
334
                                     (r['country'],
 
335
                                     False,
 
336
                                     0,
 
337
                                     r['country'].lower().translate(normalize_tb)))
 
338
            if state != r['state']:
 
339
                state_i = s.append(country_i,(r['state'],
 
340
                                   False,
 
341
                                   0,
 
342
                                   r['state'].lower().translate(normalize_tb)))
 
343
            country = r['country']
 
344
            state = r['state']
 
345
            if r['locale_name']:
 
346
                city=u'%s - %s' % (r['name'], r['locale_name'])
 
347
            else:
 
348
                city=r['name']
 
349
            city_i = s.append(state_i,(city,
 
350
                                      True,
 
351
                                      r['id'],
 
352
                                      city.lower().translate(normalize_tb)))
 
353
            if self.applet.conf.get('city_id',None) == r['id']:
 
354
                city_path = s.get_path(city_i)
 
355
        if city_path:
 
356
            tree.expand_to_path(city_path)
 
357
            tree.get_selection().select_path(city_path)
 
358
            tree.scroll_to_cell(city_path)
 
359
 
 
360
    def run(self, *a, **kw):
 
361
        self.auto_start.set_active(not os.path.exists(self.applet.skip_auto_fn))
 
362
        return Gtk.Dialog.run(self, *a, **kw)
242
363
 
243
364
class applet(object):
244
 
  locale_re=re.compile('^[a-z]+_[A-Z]+$', re.I)
245
 
  skip_auto_fn=os.path.expanduser('~/.monajat-applet-skip-auto')
246
 
  
247
 
  def _init_locale(self, lang):
248
 
    try: l=locale.setlocale(locale.LC_MESSAGES, (lang, 'UTF-8'))
249
 
    except: pass
250
 
    else:
251
 
      if l: os.environ['LC_MESSAGES']=l
252
 
      return
253
 
    for l in locale.locale_alias.keys():
254
 
      if not l.startswith(lang+'_') or not self.locale_re.match(l): continue
255
 
      l,c=l.split('_',1)
256
 
      l=l+"_"+c.upper()+".UTF-8"
257
 
      try: locale.setlocale(locale.LC_MESSAGES, l)
258
 
      except locale.Error: pass
259
 
      else:
260
 
          os.environ['LC_MESSAGES']=l
261
 
          return
262
 
 
263
 
  def __init__(self):
264
 
    self.conf_dlg=None
265
 
    self.chngbody=time.time()
266
 
    self.minutes_counter=0
267
 
    self.m=Monajat() # you may pass width like 20 chars
268
 
    self.sound_player=SoundPlayer()
269
 
    self.load_conf()
270
 
    self.prayer_items=[]
271
 
    kw=self.conf_to_prayer_args()
272
 
    self.prayer=itl.PrayerTimes(**kw)
273
 
    self._init_locale(self.m.lang)
274
 
    ld=os.path.join(self.m.get_prefix(),'..','locale')
275
 
    gettext.install('monajat', ld, unicode=0)
276
 
    self.ptnames=[_("Fajr"), _("Sunrise"), _("Dhuhr"), _("Asr"), _("Maghrib"), _("Isha'a")]
277
 
    self.clip1=gtk.Clipboard(selection="CLIPBOARD")
278
 
    self.clip2=gtk.Clipboard(selection="PRIMARY")
279
 
    self.init_about_dialog()
280
 
    self.init_menu()
281
 
    #self.s=gtk.status_icon_new_from_file(os.path.join(self.m.get_prefix(),'monajat.svg'))
282
 
    #self.s.connect('popup-menu',self.popup_cb)
283
 
    #self.s.connect('activate',self.next_cb)
284
 
    pynotify.init('MonajatApplet')
285
 
    self.notifycaps = pynotify.get_server_caps ()
286
 
    print self.notifycaps
287
 
    self.notify=pynotify.Notification("MonajatApplet")
288
 
    self.notify.set_property('icon-name','monajat')
289
 
    self.notify.set_property('summary', _("Monajat") )
290
 
    if 'actions' in self.notifycaps:
291
 
      self.notify.add_action("previous", _("previous"), self.notify_cb)
292
 
      self.notify.add_action("next", _("next"), self.notify_cb)
293
 
      self.notify.add_action("copy", _("copy"), self.notify_cb)
294
 
    # FIXME: maybe we should have notify object per notification
295
 
    #self.notify.set_timeout(5000) # FIXME: commented out to fix https://bugs.launchpad.net/ubuntu/+source/monajat/+bug/844680
296
 
    #self.notify.set_urgency(pynotify.URGENCY_LOW)
297
 
    self.notify.set_hint('resident', True)
298
 
    #self.notify.set_hint('transient', True)
299
 
 
300
 
    self.statusicon = gtk.StatusIcon ()
301
 
    #self.notify.attach_to_status_icon(self.statusicon)
302
 
    self.statusicon.connect('popup-menu',self.popup_cb)
303
 
    self.statusicon.connect('activate',self.next_cb)
304
 
    self.statusicon.set_title(_("Monajat"))
305
 
    self.statusicon.set_from_file(os.path.join(self.m.get_prefix(),'monajat.svg'))
306
 
 
307
 
    self.notif_last_athan=-1
308
 
    self.next_athan_delta=-1
309
 
    self.next_athan_i=-1
310
 
    self.last_athan=-1
311
 
    self.last_time=0
312
 
    self.first_notif_done=False
313
 
    self.start_time=time.time()
314
 
    glib.timeout_add_seconds(1, self.timer_cb)
315
 
 
316
 
  def config_cb(self, *a):
317
 
    if self.conf_dlg==None:
318
 
      self.conf_dlg=ConfigDlg(self)
319
 
      self.conf_dlg.show_all()
320
 
    r=self.conf_dlg.run()
321
 
    if r==gtk.RESPONSE_OK:
322
 
      self.save_auto_start()
323
 
      self.save_conf()
324
 
 
325
 
  def default_conf(self):
326
 
    self.conf={}
327
 
    self.conf['show_merits']='1'
328
 
    self.conf['lang']=self.m.lang
329
 
    self.conf['minutes']='10'
330
 
    self.conf['athan_media_file']=os.path.join(self.m.prefix, 'athan.ogg')
331
 
    self.conf['notify_before_min']='10'
332
 
 
333
 
  def conf_to_prayer_args(self):
334
 
    kw={}
335
 
    if not self.conf.has_key('city_id'): return kw
336
 
    c=self.m.cities_c
337
 
    try: c_id=int(self.conf['city_id'])
338
 
    except ValueError: return kw
339
 
    except TypeError: return kw
340
 
    r=c.execute('SELECT * FROM cities AS c LEFT JOIN dst AS d ON d.i=dst_id WHERE c.id=?', (c_id,)).fetchone()
341
 
    # FIXME: if not r: defaults to Makka
342
 
    kw=dict(r)
343
 
    if "alt" not in kw or not kw["alt"]: kw["alt"]=0.0
344
 
    kw["tz"]=kw["utc"]
345
 
    # NOTE: get DST from machine local setting
346
 
    kw["dst"]=time.localtime().tm_isdst
347
 
    # FIXME: dst should have the following 3 options
348
 
    # a. auto from system, b. auto from algorithm, c. specified to 0/1 by user
349
 
    #dst=kw["dst_id"]
350
 
    #if not dst: kw["dst"]=0
351
 
    #else:
352
 
    #  # FIXME: add DST logic here
353
 
    #  kw["dst"]=1
354
 
    print kw
355
 
    #lat=21.43, lon=39.77, tz=3.0, dst=0, alt=0, pressure=1010, temp=10
356
 
    return kw
357
 
 
358
 
 
359
 
  def parse_conf(self, s):
360
 
    self.default_conf()
361
 
    l1=map(lambda k: k.split('=',1), filter(lambda j: j,map(lambda i: i.strip(),s.splitlines())) )
362
 
    l2=map(lambda a: (a[0].strip(),a[1].strip()),filter(lambda j: len(j)==2,l1))
363
 
    r=dict(l2)
364
 
    self.conf.update(dict(l2))
365
 
    return len(l1)==len(l2)
366
 
 
367
 
  def load_conf(self):
368
 
    s=''
369
 
    fn=os.path.expanduser('~/.monajat-applet.rc')
370
 
    if os.path.exists(fn):
371
 
      try: s=open(fn,'rt').read()
372
 
      except OSError: pass
373
 
    self.parse_conf(s)
374
 
    if self.conf.has_key('city_id'):
375
 
      # make sure city_id is set using the same db
376
 
      if not self.conf.has_key('cities_db_ver') or \
377
 
        self.conf['cities_db_ver']!=self.m.cities_db_ver:
378
 
          del self.conf['city_id']
379
 
      # make sure it's integer
380
 
      try: c_id=int(self.conf['city_id'])
381
 
      except ValueError: del self.conf['city_id']
382
 
      except TypeError: del self.conf['city_id']
383
 
      else: self.conf['city_id']=c_id
384
 
    # set athan media file exists
385
 
    self.sound_player.set_filename(self.conf['athan_media_file'])
386
 
    # fix types
387
 
    try: self.conf['minutes']=math.ceil(float(self.conf['minutes'])/5.0)*5
388
 
    except ValueError: self.conf['minutes']=0
389
 
    try: self.conf['show_merits']=int(self.conf['show_merits']) 
390
 
    except ValueError: self.conf['show_merits']=1
391
 
    try: self.conf['notify_before_min']=int(float(self.conf['notify_before_min']))
392
 
    except ValueError: self.conf['notify_before_min']=10
393
 
    self.m.set_lang(self.conf['lang'])
394
 
    self.m.clear()
395
 
 
396
 
  def apply_conf(self):
397
 
    kw=self.conf_to_prayer_args()
398
 
    self.prayer=itl.PrayerTimes(**kw)
399
 
    self.update_prayer()
400
 
    self.sound_player.set_filename(self.conf['athan_media_file'])
401
 
    self.m.clear()
402
 
    self.m.set_lang(self.conf['lang'])
403
 
    self.render_body(self.m.go_forward())
404
 
 
405
 
  def save_conf(self):
406
 
    self.conf['cities_db_ver']=self.m.cities_db_ver
407
 
    self.conf['show_merits']=int(self.conf_dlg.show_merits.get_active())
408
 
    self.conf['lang']=self.conf_dlg.lang.get_active_text()
409
 
    self.conf['minutes']=int(self.conf_dlg.timeout.get_value())
410
 
    self.conf['athan_media_file']=self.conf_dlg.sound_file.get_filename()
411
 
    self.conf['notify_before_min']=int(self.conf_dlg.notify_before_b.get_active() and self.conf_dlg.notify_before_t.get_value())
412
 
    m, p=self.conf_dlg.cities_tree.get_selection().get_selected_rows()
413
 
    if p:
414
 
      city_id=m[p[0]][2]
415
 
      if city_id: self.conf['city_id']=city_id
416
 
    print "** saving conf", self.conf
417
 
    fn=os.path.expanduser('~/.monajat-applet.rc')
418
 
    s='\n'.join(map(lambda k: "%s=%s" % (k,str(self.conf[k])), self.conf.keys()))
419
 
    try: open(fn,'wt').write(s)
420
 
    except OSError: pass
421
 
    self.apply_conf()
422
 
 
423
 
  def athan_show_notif(self):
424
 
    self.last_time=time.time()
425
 
    self.first_notif_done=True
426
 
    s=self.ptnames[self.last_athan]
427
 
    self.notify.set_property('body', _('''It's now time for %s prayer''') % s)
428
 
    self.notify.show()
429
 
 
430
 
  def athan_notif_cb(self):
431
 
    i, t, dt=self.prayer.get_next_time_stamp()
432
 
    if i<0: return False
433
 
    dt=max(dt, 0)
434
 
    i=i%6
435
 
    self.next_athan_delta=dt
436
 
    self.next_athan_i=i
437
 
    if dt<30 and i!=self.last_athan:
438
 
      print "it's time for prayer number:", i
439
 
      self.last_athan=i
440
 
      self.sound_player.play()
441
 
      self.athan_show_notif()
442
 
      return True
443
 
    return False
444
 
 
445
 
  def timer_cb(self, *args):
446
 
    dt=int(time.time()-self.last_time)
447
 
    if self.prayer.update():
448
 
      self.update_prayer()
449
 
    if self.athan_notif_cb(): return True
450
 
    if not self.first_notif_done:
451
 
      if int(time.time()-self.start_time)>=10:
 
365
    locale_re = re.compile('^[a-z]+_[A-Z]+$', re.I)
 
366
    skip_auto_fn = os.path.expanduser('~/.monajat-applet-skip-auto')
 
367
    
 
368
    def _init_locale(self, lang):
 
369
        try:
 
370
            l = locale.setlocale(locale.LC_MESSAGES, (lang, 'UTF-8'))
 
371
        except:
 
372
            pass
 
373
        else:
 
374
            if l:
 
375
                os.environ['LC_MESSAGES'] = l
 
376
            return
 
377
        for l in locale.locale_alias.keys():
 
378
            if not l.startswith(lang+'_') or not self.locale_re.match(l):
 
379
                continue
 
380
            l, c = l.split('_',1)
 
381
            l = l + "_" + c.upper() + ".UTF-8"
 
382
            try:
 
383
                locale.setlocale(locale.LC_MESSAGES, l)
 
384
            except locale.Error:
 
385
                pass
 
386
            else:
 
387
                os.environ['LC_MESSAGES'] = l
 
388
                return
 
389
 
 
390
    def __init__(self):
 
391
        self.conf_dlg = None
 
392
        self.chngbody = time.time()
 
393
        self.minutes_counter = 0
 
394
        self.m = Monajat() # you may pass width like 20 chars
 
395
        self.sound_player = SoundPlayer()
 
396
        self.load_conf()
 
397
        self.prayer_items = []
 
398
        kw = self.conf_to_prayer_args()
 
399
        self.prayer = itl.PrayerTimes(**kw)
 
400
        self._init_locale(self.m.lang)
 
401
        ld = os.path.join(self.m.get_prefix(), '..', 'locale')
 
402
        gettext.install('monajat', ld, unicode=0)
 
403
        self.ptnames = [_("Fajr"),
 
404
                        _("Sunrise"),
 
405
                        _("Dhuhr"),
 
406
                        _("Asr"),
 
407
                        _("Maghrib"),
 
408
                        _("Isha'a")]
 
409
        self.clip1 = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
 
410
        self.clip2 = Gtk.Clipboard.get(Gdk.SELECTION_PRIMARY)
 
411
        self.init_menu()
 
412
        
 
413
 
 
414
        self.statusicon = Gtk.StatusIcon ()
 
415
        self.statusicon.connect('popup-menu',self.popup_cb)
 
416
        self.statusicon.connect('activate',self.next_cb)
 
417
        self.statusicon.set_title(_("Monajat"))
 
418
        self.statusicon.set_from_file(os.path.join(self.m.get_prefix(),'monajat.svg'))
 
419
        
 
420
        Notify.init(_("Monajat"))
 
421
        self.init_notify_cb()
 
422
        print self.notifycaps
 
423
        
 
424
        self.notif_last_athan = -1
 
425
        self.next_athan_delta = -1
 
426
        self.next_athan_i = -1
 
427
        self.last_athan = -1
 
428
        self.last_time = 0
 
429
        self.first_notif_done = False
 
430
        self.start_time = time.time()
 
431
        GObject.timeout_add(1, self.timer_cb)
 
432
 
 
433
    def init_notify_cb(self):
 
434
        self.notifycaps = Notify.get_server_caps ()
 
435
        self.notify = Notify.Notification()
 
436
        self.notify.set_property('icon-name', os.path.join(self.m.get_prefix(),'monajat.svg'))
 
437
        self.notify.set_property('summary', _("Monajat") )
 
438
        if 'actions' in self.notifycaps:
 
439
            self.notify.add_action("previous", _("Back"), self.notify_cb, None)
 
440
            self.notify.add_action("next", _("Forward"), self.notify_cb, None)
 
441
            self.notify.add_action("copy", _("copy"), self.notify_cb, None)
 
442
        
 
443
    def show_notify_cb(self, body, *args):
 
444
        self.notify.set_property('body', body )
 
445
        self.notify.show()
 
446
        
 
447
    def config_cb(self, *a):
 
448
        if self.conf_dlg == None:
 
449
            self.conf_dlg = ConfigDlg(self)
 
450
            self.conf_dlg.show_all()
 
451
        r = self.conf_dlg.run()
 
452
        if r == Gtk.ResponseType.OK:
 
453
            self.save_auto_start()
 
454
            self.save_conf()
 
455
 
 
456
    def default_conf(self):
 
457
        self.conf = {}
 
458
        self.conf['show_merits'] = '1'
 
459
        self.conf['lang'] = self.m.lang
 
460
        self.conf['minutes'] = '10'
 
461
        self.conf['athan_media_file'] = os.path.join(self.m.prefix, 'athan.ogg')
 
462
        print self.conf['athan_media_file']
 
463
        self.conf['notify_before_min'] = '10'
 
464
 
 
465
    def conf_to_prayer_args(self):
 
466
        kw = {}
 
467
        if not self.conf.has_key('city_id'): return kw
 
468
        c = self.m.cities_c
 
469
        try:
 
470
            c_id = int(self.conf['city_id'])
 
471
        except ValueError:
 
472
            return kw
 
473
        except TypeError:
 
474
            return kw
 
475
        r = c.execute('SELECT * FROM cities AS c LEFT JOIN dst AS d ON d.i=dst_id WHERE c.id=?', (c_id,)).fetchone()
 
476
        # set defaults to Makkah (14244)
 
477
        if not r:
 
478
            r = c.execute('SELECT * FROM cities AS c LEFT JOIN dst AS d ON d.i=dst_id WHERE c.id=?',
 
479
                          (14244,)).fetchone()
 
480
        kw = dict(r)
 
481
        if "alt" not in kw or not kw["alt"]:
 
482
            kw["alt"] = 0.0
 
483
        kw["tz"] = kw["utc"]
 
484
        # NOTE: get DST from machine local setting
 
485
        kw["dst"] = time.localtime().tm_isdst
 
486
        # FIXME: dst should have the following 3 options
 
487
        # a. auto from system, b. auto from algorithm, c. specified to 0/1 by user
 
488
        #dst=kw["dst_id"]
 
489
        #if not dst: kw["dst"]=0
 
490
        #else:
 
491
        #    # FIXME: add DST logic here
 
492
        #    kw["dst"]=1
 
493
        print kw
 
494
        #lat=21.43, lon=39.77, tz=3.0, dst=0, alt=0, pressure=1010, temp=10
 
495
        return kw
 
496
 
 
497
 
 
498
    def parse_conf(self, s):
 
499
        self.default_conf()
 
500
        l1 = map(lambda k: k.split('=',1),
 
501
                 filter(lambda j: j,
 
502
                 map(lambda i: i.strip(),s.splitlines())) )
 
503
        l2 = map(lambda a: (a[0].strip(),a[1].strip()),filter(lambda j: len(j)==2,l1))
 
504
        r = dict(l2)
 
505
        self.conf.update(dict(l2))
 
506
        return len(l1) == len(l2)
 
507
 
 
508
    def load_conf(self):
 
509
        s = ''
 
510
        fn = os.path.expanduser('~/.monajat-applet.rc')
 
511
        if os.path.exists(fn):
 
512
            try:
 
513
                s = open(fn,'rt').read()
 
514
            except OSError:
 
515
                pass
 
516
        self.parse_conf(s)
 
517
        if self.conf.has_key('city_id'):
 
518
            # make sure city_id is set using the same db
 
519
            if not self.conf.has_key('cities_db_ver') \
 
520
               or self.conf['cities_db_ver'] != self.m.cities_db_ver:
 
521
                   del self.conf['city_id']
 
522
            # make sure it's integer
 
523
            try:
 
524
                c_id = int(self.conf['city_id'])
 
525
            except ValueError:
 
526
                self.conf['city_id'] =  14244
 
527
            except TypeError:
 
528
                self.conf['city_id'] = 14244
 
529
            else:
 
530
                self.conf['city_id'] = c_id
 
531
        else:
 
532
            # set default to makkah (14244)
 
533
            self.conf['city_id'] = 14244
 
534
        # set athan media file exists
 
535
        self.sound_player.set_filename(self.conf['athan_media_file'])
 
536
        # fix types
 
537
        try:
 
538
            self.conf['minutes'] = math.ceil(float(self.conf['minutes'])/5.0)*5
 
539
        except ValueError:
 
540
            self.conf['minutes'] = 0
 
541
        try:
 
542
            self.conf['show_merits'] = int(self.conf['show_merits']) 
 
543
        except ValueError:
 
544
            self.conf['show_merits'] = 1
 
545
        try:
 
546
            self.conf['notify_before_min'] = int(float(self.conf['notify_before_min']))
 
547
        except ValueError:
 
548
            self.conf['notify_before_min'] = 10
 
549
        self.m.set_lang(self.conf['lang'])
 
550
        self.m.clear()
 
551
 
 
552
    def apply_conf(self):
 
553
        kw = self.conf_to_prayer_args()
 
554
        self.prayer = itl.PrayerTimes(**kw)
 
555
        self.update_prayer()
 
556
        self.sound_player.set_filename(self.conf['athan_media_file'])
 
557
        self.m.clear()
 
558
        self.m.set_lang(self.conf['lang'])
 
559
        self.render_body(self.m.go_forward())
 
560
 
 
561
    def save_conf(self):
 
562
        self.conf['cities_db_ver'] = self.m.cities_db_ver
 
563
        self.conf['show_merits'] = int(self.conf_dlg.show_merits.get_active())
 
564
        self.conf['lang'] = self.conf_dlg.lang.get_active_text()
 
565
        self.conf['minutes'] = int(self.conf_dlg.timeout.get_value())
 
566
        self.conf['athan_media_file'] = self.conf_dlg.sound_file.get_filename()
 
567
        self.conf['notify_before_min'] = int(self.conf_dlg.notify_before_b.get_active() \
 
568
                                            and self.conf_dlg.notify_before_t.get_value())
 
569
        m, p = self.conf_dlg.cities_tree.get_selection().get_selected_rows()
 
570
        if p:
 
571
            city_id = m[p[0]][2]
 
572
            if city_id:
 
573
                self.conf['city_id'] = city_id
 
574
        print "** saving conf", self.conf
 
575
        fn = os.path.expanduser('~/.monajat-applet.rc')
 
576
        s = '\n'.join(map(lambda k: "%s=%s" % (k,str(self.conf[k])), self.conf.keys()))
 
577
        try:
 
578
            open(fn,'wt').write(s)
 
579
        except OSError:
 
580
            pass
 
581
        self.apply_conf()
 
582
 
 
583
    def athan_show_notif(self):
 
584
        self.last_time = time.time()
 
585
        self.first_notif_done = True
 
586
        s = self.ptnames[self.last_athan]
 
587
        self.show_notify_cb(_('''It's now time for %s prayer''') % s)
 
588
        return True
 
589
 
 
590
    def athan_notif_cb(self):
 
591
        i, t, dt = self.prayer.get_next_time_stamp()
 
592
        if i < 0:
 
593
            return False
 
594
        dt = max(dt, 0)
 
595
        i = i%6
 
596
        self.next_athan_delta = dt
 
597
        self.next_athan_i = i
 
598
        if dt < 30 and i != self.last_athan:
 
599
            print "it's time for prayer number:", i
 
600
            self.last_athan = i
 
601
            self.sound_player.play()
 
602
            self.athan_show_notif()
 
603
            return True
 
604
        return False
 
605
 
 
606
    def timer_cb(self, *args):
 
607
        if not 'actions' in self.notifycaps:
 
608
            self.init_notify_cb()
 
609
            print self.notifycaps
 
610
        dt = int(time.time()-self.last_time)
 
611
        if self.prayer.update():
 
612
            self.update_prayer()
 
613
        if self.athan_notif_cb(): return True
 
614
        if not self.first_notif_done:
 
615
            if int(time.time()-self.start_time) >= 10:
 
616
                self.next_cb()
 
617
            return True
 
618
        if self.conf['notify_before_min'] \
 
619
           and self.next_athan_delta <= self.conf['notify_before_min']*60 \
 
620
           and self.next_athan_i != self.notif_last_athan:
 
621
               self.notif_last_athan = self.next_athan_i
 
622
               self.next_cb()
 
623
        elif self.conf['minutes'] and dt >= self.conf['minutes']*60:
 
624
               self.next_cb()
 
625
        return True
 
626
 
 
627
    def fuzzy_delta(self):
 
628
        if self.next_athan_i < 0 :
 
629
            return ""
 
630
        t = max(int(self.next_athan_delta), 0)
 
631
        d = {"prayer" : self.ptnames[self.next_athan_i]}
 
632
        d['hours'] = t/3600
 
633
        t %= 3600
 
634
        d['minutes'] = t/60
 
635
        if d["hours"]:
 
636
            if d["minutes"] < 5:
 
637
                r = _("""%(hours)d hours till %(prayer)s prayer""") % d
 
638
            else:
 
639
                r = _("""%(hours)d hours and %(minutes)d minutes till %(prayer)s prayer""") % d
 
640
        elif d["minutes"] >= 2:
 
641
            r = _("""less than %(minutes)d minutes till %(prayer)s prayer""") % d
 
642
        else:
 
643
            r = _("""less than a minute till %(prayer)s prayer""") % d
 
644
        if "body-markup" in self.notifycaps:
 
645
            return "<b>%s</b>\n\n" % cgi.escape(r)
 
646
        return "%s\n\n" % r
 
647
 
 
648
    def body_to_str(self, body):
 
649
        if type(body) == unicode:
 
650
            return body.encode('utf-8')
 
651
        return body
 
652
        
 
653
    def render_body(self, m):
 
654
        merits = m['merits']
 
655
        if not self.conf['show_merits']:
 
656
            merits = None
 
657
        if "body-markup" in self.notifycaps:
 
658
            body = cgi.escape(m['text'])
 
659
            if merits:
 
660
                body = """{}\n\n<b>{}</b>: {}""".format(self.body_to_str(body),
 
661
                                                        _("Its Merits"),
 
662
                                                        self.body_to_str(cgi.escape(merits)))
 
663
        else:
 
664
            body = m['text']
 
665
            if merits:
 
666
                body = """{}\n\n** {} **: {}""".format(self.body_to_str(body),
 
667
                                                       _("Its Merits"),
 
668
                                                       self.body_to_str(merits))
 
669
        if type(body) == unicode:
 
670
                body = body.encode('utf-8')
 
671
        if "body-hyperlinks" in self.notifycaps:
 
672
            L = []
 
673
            links = m.get('links',u'').split(u'\n')
 
674
            for l in links:
 
675
                ll = l.split(u'\t',1)
 
676
                url = cgi.escape(ll[0])
 
677
                if len(ll) > 1:
 
678
                    t = cgi.escape(ll[1])
 
679
                else:
 
680
                    t = url
 
681
                print url, t
 
682
                L.append(u"""<a href='{}'>{}</a>""".format(url,t))
 
683
            l = u"\n\n".join(L)
 
684
            body += self.body_to_str("\n\n" + l)
 
685
        # if we are close to time show it before supplication
 
686
        if self.next_athan_delta >= 0 and self.next_athan_delta <= 600:
 
687
            body = self.fuzzy_delta() + self.body_to_str(body)
 
688
        else:
 
689
            body = """{}\n\n{}""".format(self.body_to_str(body), self.fuzzy_delta())
 
690
            #body = body + "\n\n" + self.fuzzy_delta()
 
691
        #timeNP,isnear, istime = self.nextprayer_note(self.get_nextprayer(self.prayer.get_prayers()))
 
692
        #if not isnear: body+=timeNP
 
693
        #else: body=timeNP+body
 
694
        self.show_notify_cb(body)
 
695
        return body
 
696
 
 
697
    def dbus_cb(self, *args):
 
698
        self.minutes_counter = 1
452
699
        self.next_cb()
453
 
      return True
454
 
    if self.conf['notify_before_min'] and self.next_athan_delta<=self.conf['notify_before_min']*60 and self.next_athan_i!=self.notif_last_athan:
455
 
      self.notif_last_athan=self.next_athan_i
456
 
      self.next_cb()
457
 
    elif self.conf['minutes'] and dt>=self.conf['minutes']*60:
458
 
      self.next_cb()
459
 
    return True
460
 
 
461
 
  def hide_cb(self, w, *args): w.hide(); return True
462
 
 
463
 
  def fuzzy_delta(self):
464
 
    if self.next_athan_i<0: return ""
465
 
    t=max(int(self.next_athan_delta),0)
466
 
    d={"prayer":self.ptnames[self.next_athan_i]}
467
 
    d['hours']=t/3600
468
 
    t%=3600
469
 
    d['minutes']=t/60
470
 
    if d["hours"]:
471
 
      if d["minutes"]<5:
472
 
        r=_("""%(hours)d hours till %(prayer)s prayer""") % d
473
 
      else:
474
 
        r=_("""%(hours)d hours and %(minutes)d minutes till %(prayer)s prayer""") % d
475
 
    elif d["minutes"]>=2:
476
 
      r=_("""less than %(minutes)d minutes till %(prayer)s prayer""") % d
477
 
    else:
478
 
      r=_("""less than a minute till %(prayer)s prayer""") % d
479
 
    if "body-markup" in self.notifycaps:
480
 
      return "<b>%s</b>\n\n" % cgi.escape(r)
481
 
    return "%s\n\n" % r
482
 
 
483
 
  def render_body(self, m):
484
 
    merits=m['merits']
485
 
    if not self.conf['show_merits']: merits=None
486
 
    if "body-markup" in self.notifycaps:
487
 
      body=cgi.escape(m['text'])
488
 
      if merits: body+="""\n\n<b>%s</b>: %s""" % (_("Its Merits"),cgi.escape(merits))
489
 
    else:
490
 
      body=m['text']
491
 
      if merits: body+="""\n\n** %s **: %s""" % (_("Its Merits"),merits)
492
 
    
493
 
    if "body-hyperlinks" in self.notifycaps:
494
 
      L=[]
495
 
      links=m.get('links',u'').split(u'\n')
496
 
      for l in links:
497
 
        ll=l.split(u'\t',1)
498
 
        url=cgi.escape(ll[0])
499
 
        if len(ll)>1: t=cgi.escape(ll[1])
500
 
        else: t=url
501
 
        L.append(u"""<a href='%s'>%s</a>""" % (url,t))
502
 
      l=u"\n\n".join(L)
503
 
      body+=u"\n\n"+l
504
 
    # if we are close to time show it before supplication
505
 
    if self.next_athan_delta>=0 and self.next_athan_delta<=600:
506
 
      body=self.fuzzy_delta()+body
507
 
    else:
508
 
      body=body+"\n\n"+self.fuzzy_delta()
509
 
    #timeNP,isnear, istime = self.nextprayer_note(self.get_nextprayer(self.prayer.get_prayers()))
510
 
    #if not isnear: body+=timeNP
511
 
    #else: body=timeNP+body
512
 
    self.notify.set_property('body', body)
513
 
    return body
514
 
 
515
 
  def dbus_cb(self, *args):
516
 
    self.minutes_counter=1
517
 
    self.next_cb()
518
 
    return 0
519
 
 
520
 
  def next_cb(self,*args):
521
 
    self.last_time=time.time()
522
 
    self.first_notif_done=True
523
 
    try: self.notify.close()
524
 
    except glib.GError: pass
525
 
    self.render_body(self.m.go_forward())
526
 
    self.notify.show()
527
 
    return True
528
 
 
529
 
  def prev_cb(self, *args):
530
 
    self.last_time=time.time()
531
 
    try: self.notify.close()
532
 
    except glib.GError: pass
533
 
    self.render_body(self.m.go_back())
534
 
    self.notify.show()
535
 
 
536
 
  def copy_cb(self,*args):
537
 
    r=self.m.get_current()
538
 
    self.clip1.set_text(r['text'])
539
 
    self.clip2.set_text(r['text'])
540
 
 
541
 
  def init_about_dialog(self):
542
 
    # FIXME: please add more the authors
543
 
    self.about_dlg=gtk.AboutDialog()
544
 
    self.about_dlg.set_default_response(gtk.RESPONSE_CLOSE)
545
 
    self.about_dlg.connect('delete-event', self.hide_cb)
546
 
    self.about_dlg.connect('response', self.hide_cb)
547
 
    try: self.about_dlg.set_program_name("Monajat")
548
 
    except: pass
549
 
    self.about_dlg.set_name(_("Monajat"))
550
 
    #self.about_dlg.set_version(version)
551
 
    self.about_dlg.set_copyright("Copyright © 2009 sabily.org")
552
 
    self.about_dlg.set_comments(_("Monajat supplications"))
553
 
    self.about_dlg.set_license("""\
554
 
    This program is free software; you can redistribute it and/or modify
555
 
    it under the terms of the GNU General Public License as published by
556
 
    the Free Software Foundation; either version 2 of the License, or
557
 
    (at your option) any later version.
558
 
 
559
 
    This program is distributed in the hope that it will be useful,
560
 
    but WITHOUT ANY WARRANTY; without even the implied warranty of
561
 
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
562
 
    GNU General Public License for more details.
 
700
        return 0
 
701
 
 
702
    def next_cb(self, *args):
 
703
        self.last_time = time.time()
 
704
        self.first_notif_done = True
 
705
        self.render_body(self.m.go_forward())
 
706
        return True
 
707
 
 
708
    def prev_cb(self, *args):
 
709
        self.first_notif_done = True
 
710
        self.render_body(self.m.go_back())
 
711
        return True
 
712
 
 
713
    def copy_cb(self, *args):
 
714
        r = self.m.get_current()
 
715
        self.clip1.set_text(r['text'], -1)
 
716
        self.clip2.set_text(r['text'], -1)
 
717
 
 
718
    def show_about_dialog(self):
 
719
        # FIXME: please add more the authors
 
720
        dlg = Gtk.AboutDialog()
 
721
        dlg.set_default_response(Gtk.ResponseType.CLOSE)
 
722
        dlg.set_icon_from_file(os.path.join(self.m.get_prefix(),'monajat.svg'))
 
723
        try:
 
724
            dlg.set_program_name("Monajat")
 
725
        except:
 
726
            pass
 
727
        dlg.set_name(_("Monajat"))
 
728
        #dlg.set_version(version)
 
729
        dlg.set_copyright("Copyright © 2009 sabily.org")
 
730
        dlg.set_comments(_("Monajat supplications"))
 
731
        dlg.set_license("""\
 
732
        This program is free software; you can redistribute it and/or modify
 
733
        it under the terms of the GNU General Public License as published by
 
734
        the Free Software Foundation; either version 2 of the License, or
 
735
        (at your option) any later version.
 
736
 
 
737
        This program is distributed in the hope that it will be useful,
 
738
        but WITHOUT ANY WARRANTY; without even the implied warranty of
 
739
        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.    See the
 
740
        GNU General Public License for more details.
563
741
""")
564
 
    self.about_dlg.set_website("https://launchpad.net/monajat")
565
 
    self.about_dlg.set_website_label("Monajat web site")
566
 
    self.about_dlg.set_authors(["Fadi al-katout <cutout33@gmail.com>",
567
 
                                "Muayyad Saleh Alsadi <alsadi@ojuba.org>",
568
 
                                "Ehab El-Gedawy <ehabsas@gmail.com>",
569
 
                                "Mahyuddin Susanto <udienz@ubuntu.com>",
570
 
                                "عبدالرحيم دوبيلار <abdulrahiem@sabi.li>",
571
 
                                "أحمد المحمودي (Ahmed El-Mahmoudy) <aelmahmoudy@sabily.org>"])
572
 
  def save_auto_start(self):
573
 
    b=self.conf_dlg.auto_start.get_active()
574
 
    if b and os.path.exists(self.skip_auto_fn):
575
 
      try: os.unlink(self.skip_auto_fn)
576
 
      except OSError: pass
577
 
    elif not b:
578
 
      open(self.skip_auto_fn,'wt').close()
579
 
  
580
 
  def update_prayer(self):
581
 
    if not self.prayer_items: return
582
 
    pt=self.prayer.get_prayers()
583
 
    j=0
584
 
    ptn=list(self.ptnames)
585
 
    ptn[1]=''
586
 
    for p,t in zip(ptn, pt):
587
 
      if not p: continue
588
 
      i = gtk.MenuItem
589
 
      self.prayer_items[j].set_label(u"%-10s %s" % (p, t.format(),))
590
 
      j+=1
591
 
  
592
 
  def init_menu(self):
593
 
    self.menu = gtk.Menu()
594
 
    i = gtk.ImageMenuItem(gtk.STOCK_COPY)
595
 
    i.set_always_show_image(True)
596
 
    i.connect('activate', self.copy_cb)
597
 
    self.menu.add(i)
598
 
 
599
 
    i = gtk.ImageMenuItem(gtk.STOCK_GO_FORWARD)
600
 
    i.set_always_show_image(True)
601
 
    i.connect('activate', self.next_cb)
602
 
    self.menu.add(i)
603
 
 
604
 
    i = gtk.ImageMenuItem(gtk.STOCK_GO_BACK)
605
 
    i.set_always_show_image(True)
606
 
    i.connect('activate', self.prev_cb)
607
 
    self.menu.add(i)
608
 
 
609
 
    self.menu.add(gtk.SeparatorMenuItem())
610
 
 
611
 
    self.prayer_items=[]
612
 
    for j in range(5):
613
 
      i = gtk.MenuItem("-")
614
 
      self.prayer_items.append(i)
615
 
      self.menu.add(i)
616
 
    self.update_prayer()
617
 
 
618
 
    self.menu.add(gtk.SeparatorMenuItem())
619
 
    
620
 
    i = gtk.MenuItem(_("Configure"))
621
 
    i.connect('activate', self.config_cb)
622
 
    self.menu.add(i)
623
 
    
624
 
    self.menu.add(gtk.SeparatorMenuItem())
625
 
 
626
 
    i = gtk.ImageMenuItem(gtk.STOCK_ABOUT)
627
 
    i.set_always_show_image(True)
628
 
    i.connect('activate', lambda *args: self.about_dlg.run())
629
 
    self.menu.add(i)
630
 
    
631
 
    i = gtk.ImageMenuItem(gtk.STOCK_QUIT)
632
 
    i.set_always_show_image(True)
633
 
    i.connect('activate', gtk.main_quit)
634
 
    self.menu.add(i)
635
 
 
636
 
    self.menu.show_all()
637
 
 
638
 
  def popup_cb(self, s, button, time):
639
 
    self.menu.popup(None, None, gtk.status_icon_position_menu, button, time, s)
640
 
 
641
 
  def time_set_cb(self, m, t):
642
 
    self.conf['minutes']=t
643
 
    self.minutes_counter=1
644
 
    self.save_conf()
645
 
 
646
 
  def lang_cb(self, m, l):
647
 
    if not m.get_active(): return
648
 
    self.m.set_lang(l)
649
 
    self.save_conf()
650
 
 
651
 
  def notify_cb(self,notify,action):
652
 
    try: self.notify.close()
653
 
    except glib.GError: pass
654
 
    if action=="exit": gtk.main_quit()
655
 
    elif action=="copy": self.copy_cb()
656
 
    elif action=="next": self.next_cb()
657
 
    elif action=="previous": self.prev_cb()
 
742
        dlg.set_website("https://launchpad.net/monajat")
 
743
        dlg.set_website_label("Monajat web site")
 
744
        dlg.set_authors(["Fadi al-katout <cutout33@gmail.com>",
 
745
                                                                "Muayyad Saleh Alsadi <alsadi@ojuba.org>",
 
746
                                                                "Ehab El-Gedawy <ehabsas@gmail.com>",
 
747
                                                                "Mahyuddin Susanto <udienz@ubuntu.com>",
 
748
                                                                "عبدالرحيم دوبيلار <abdulrahiem@sabi.li>",
 
749
                                                                "أحمد المحمودي (Ahmed El-Mahmoudy) <aelmahmoudy@sabily.org>"])
 
750
        dlg.run()
 
751
        dlg.destroy()
 
752
        
 
753
    def save_auto_start(self):
 
754
        b = self.conf_dlg.auto_start.get_active()
 
755
        if b and os.path.exists(self.skip_auto_fn):
 
756
            try:
 
757
                os.unlink(self.skip_auto_fn)
 
758
            except OSError:
 
759
                pass
 
760
        elif not b:
 
761
            open(self.skip_auto_fn,'wt').close()
 
762
    
 
763
    def update_prayer(self):
 
764
        if not self.prayer_items: return
 
765
        pt = self.prayer.get_prayers()
 
766
        j = 0
 
767
        ptn = list(self.ptnames)
 
768
        ptn[1] = ''
 
769
        for p,t in zip(ptn, pt):
 
770
            if not p: continue
 
771
            i = Gtk.MenuItem
 
772
            self.prayer_items[j].set_label("{: <15} {}".format(p, t.format(),))
 
773
            j += 1
 
774
    
 
775
    def init_menu(self):
 
776
        self.menu = Gtk.Menu()
 
777
        i = Gtk.ImageMenuItem.new_from_stock(Gtk.STOCK_COPY, None)
 
778
        i.set_always_show_image(True)
 
779
        i.connect('activate', self.copy_cb)
 
780
        self.menu.add(i)
 
781
 
 
782
        i = Gtk.ImageMenuItem.new_from_stock(Gtk.STOCK_GO_FORWARD, None)
 
783
        i.set_always_show_image(True)
 
784
        i.connect('activate', self.next_cb)
 
785
        self.menu.add(i)
 
786
 
 
787
        i = Gtk.ImageMenuItem.new_from_stock(Gtk.STOCK_GO_BACK, None)
 
788
        i.set_always_show_image(True)
 
789
        i.connect('activate', self.prev_cb)
 
790
        self.menu.add(i)
 
791
 
 
792
        self.menu.add(Gtk.SeparatorMenuItem.new())
 
793
 
 
794
        self.prayer_items = []
 
795
        for j in range(5):
 
796
            i = Gtk.MenuItem("-")
 
797
            self.prayer_items.append(i)
 
798
            self.menu.add(i)
 
799
        self.update_prayer()
 
800
 
 
801
        self.menu.add(Gtk.SeparatorMenuItem.new())
 
802
        
 
803
        i = Gtk.MenuItem(_("Configure"))
 
804
        i.connect('activate', self.config_cb)
 
805
        self.menu.add(i)
 
806
        
 
807
        self.menu.add(Gtk.SeparatorMenuItem.new())
 
808
 
 
809
        i = Gtk.ImageMenuItem.new_from_stock(Gtk.STOCK_ABOUT, None)
 
810
        i.set_always_show_image(True)
 
811
        i.connect('activate', lambda *args: self.show_about_dialog()) 
 
812
        self.menu.add(i)
 
813
        
 
814
        i = Gtk.ImageMenuItem.new_from_stock(Gtk.STOCK_QUIT, None)
 
815
        i.set_always_show_image(True)
 
816
        i.connect('activate', Gtk.main_quit)
 
817
        self.menu.add(i)
 
818
 
 
819
        self.menu.show_all()
 
820
 
 
821
    def popup_cb(self, s, button, time):
 
822
 
 
823
        self.menu.popup(None,
 
824
                        None,
 
825
                        self.statusicon.position_menu,
 
826
                        self.statusicon,
 
827
                        3,
 
828
                        Gtk.get_current_event_time())
 
829
 
 
830
 
 
831
    def time_set_cb(self, m, t):
 
832
        self.conf['minutes'] = t
 
833
        self.minutes_counter = 1
 
834
        self.save_conf()
 
835
 
 
836
    def lang_cb(self, m, l):
 
837
        if not m.get_active(): return
 
838
        self.m.set_lang(l)
 
839
        self.save_conf()
 
840
 
 
841
    def notify_cb(self, notify, action, *a):
 
842
        if action == "exit":
 
843
            Gtk.main_quit()
 
844
        elif action == "copy":
 
845
            self.copy_cb()
 
846
        elif action == "next":
 
847
            self.next_cb()
 
848
        elif action == "previous":
 
849
            self.prev_cb()
658
850
 
659
851
def applet_main():
660
 
  gtk.window_set_default_icon_name('monajat')
661
 
  a=applet()
662
 
  init_dbus(a.dbus_cb)
663
 
  gtk.main()
 
852
    Gtk.Window.set_default_icon_name('monajat')
 
853
    a = applet()
 
854
    init_dbus(a.dbus_cb)
 
855
    Gtk.main()
664
856
 
665
857
if __name__ == "__main__":
666
 
  applet_main()
 
858
    applet_main()
667
859