4c0e36505ca14f9c0b0bba68214541dac015c65e
[hermes] / package / src / gui.py
1 #!/usr/bin/env python
2
3 import gtk, gobject
4 import gnome.gconf
5 import hildon, osso
6 import traceback
7 import time
8 import thread
9 import os.path
10 import contactview
11 import urllib2  
12 from hermes import Hermes
13
14 gobject.threads_init()
15
16 class HermesGUI:
17   """Provides the GUI for Hermes, allowing the syncing of Facebook and
18      Twitter friends' information with the Evolution contacts' database.
19        
20      Copyright (c) Andrew Flegg <andrew@bleb.org> 2009.
21      Released under the Artistic Licence."""
22
23
24   # -----------------------------------------------------------------------
25   def __init__(self):
26     """Constructor. Initialises the gconf connection."""
27     self.gc = gnome.gconf.client_get_default()
28
29
30   # -----------------------------------------------------------------------
31   def doSync(self, widget, force, main = True):
32     if main and not self.get_use_facebook() and not self.get_use_twitter():
33       saved = self.open_prefs()
34       if saved == gtk.RESPONSE_DELETE_EVENT:
35         return
36
37     if main:
38       self.window.set_property('sensitive', False)
39       thread.start_new_thread(self.doSync, (widget, force, False))
40     else:
41       try:
42         fb2c = Hermes(self,
43                       twitter = (self.get_use_twitter() and self.get_twitter_credentials()) or None,
44                       facebook = self.get_use_facebook(),
45                       empty = self.get_create_empty())
46         fb2c.load_friends()
47         fb2c.sync_contacts(resync = force)
48         gobject.idle_add(self.open_summary, fb2c)
49
50       except urllib2.HTTPError, e:
51         traceback.print_exc()
52         if e.code == 401:
53           gobject.idle_add(self.report_error, 'Authentication problem. Check credentials.', True)
54         else:
55           gobject.idle_add(self.report_error, 'Network connection error. Check connectivity.')
56
57       except urllib2.URLError, e:
58         traceback.print_exc()
59         gobject.idle_add(self.report_error, 'Network connection error. Check connectivity.')
60       
61       except Exception, e:
62         traceback.print_exc()
63         gobject.idle_add(self.report_error, 'Something went wrong: ' + e.message)
64
65
66   # -----------------------------------------------------------------------
67   def open_summary(self, fb2c):
68     gobject.idle_add(self.window.set_property, 'sensitive', True)
69
70     dialog = gtk.Dialog('Summary', self.window)
71     dialog.add_button('Done', gtk.RESPONSE_OK)
72     
73     button = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL,
74                            title = 'Updated %d contacts' % (len(fb2c.updated)))
75     button.connect('clicked', self.show_contacts, fb2c.updated)
76     button.set_property('sensitive', len(fb2c.updated) > 0)
77     dialog.vbox.add(button)
78     
79     button = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL,
80                            title = 'Matched %d contacts' % (len(fb2c.matched)))
81     button.connect('clicked', self.show_contacts, fb2c.matched)
82     button.set_property('sensitive', len(fb2c.matched) > 0)
83     dialog.vbox.add(button)
84     
85     button = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL,
86                            title = '%d contacts unmatched' % (len(fb2c.unmatched)))
87     button.connect('clicked', self.show_contacts, fb2c.unmatched)
88     button.set_property('sensitive', len(fb2c.unmatched) > 0)
89     dialog.vbox.add(button)
90     
91     dialog.show_all()
92     dialog.run()
93     dialog.hide()
94   
95
96   # -----------------------------------------------------------------------
97   def show_contacts(self, widget, contacts):
98     view = contactview.ContactView(contacts)
99
100     dialog = gtk.Dialog('Contacts', self.window)
101     #view.connect('contact-activated', self.map_contact)
102     dialog.vbox.add(view)
103     dialog.show_all()
104     
105     dialog.run()
106     dialog.hide()
107     
108     
109   # -----------------------------------------------------------------------
110   def map_contact(self, widget, contact):
111     print widget, contact
112
113
114   # -----------------------------------------------------------------------
115   def open_prefs(self, widget = None):
116     dialog = gtk.Dialog('Accounts', self.window)
117     dialog.add_button('Save', gtk.RESPONSE_OK)
118
119     #pa = hildon.PannableArea()
120     #dialog.vbox.add(pa)
121     content = dialog.vbox 
122     #content = gtk.VBox()
123     #pa.add(content)
124     #pa.set_size_request(600, 380)
125
126     use_facebook = self.new_checkbox('Use Facebook', content)
127     use_facebook.set_active(self.get_use_facebook())
128
129     indent = self.new_indent(content)
130     fb_msg = self.add_linked(indent, use_facebook, gtk.Label('Note: authentication via web page'))
131     fb_msg.set_property('justify', gtk.JUSTIFY_LEFT)
132     
133     fb_empty = self.add_linked(indent, use_facebook, self.new_checkbox('Create birthday-only contacts'))
134     fb_empty.set_active(self.get_create_empty())
135
136     use_twitter = self.new_checkbox('Use Twitter', content)
137     use_twitter.set_active(self.get_use_twitter())
138
139     indent = self.new_indent(content)
140     tw_user = self.add_linked(indent, use_twitter, self.new_input('Twitter username'))
141     tw_user.set_text(self.get_twitter_credentials()[0])
142
143     tw_pass = self.add_linked(indent, use_twitter, self.new_input('Twitter password'))
144     tw_pass.set_property('hildon-input-mode', gtk.HILDON_GTK_INPUT_MODE_FULL |
145                                               gtk.HILDON_GTK_INPUT_MODE_INVISIBLE)
146     tw_pass.set_text(self.get_twitter_credentials()[1])
147
148     dialog.show_all()
149     result = dialog.run()
150     dialog.hide()
151     if result == gtk.RESPONSE_OK:
152       self.set_use_facebook(use_facebook.get_active())
153       self.set_create_empty(fb_empty.get_active())
154       self.set_use_twitter(use_twitter.get_active(), tw_user.get_text(), tw_pass.get_text())
155
156     return result
157  
158   # -----------------------------------------------------------------------
159   def new_checkbox(self, label, box = None):
160     checkbox = hildon.CheckButton(gtk.HILDON_SIZE_FINGER_HEIGHT)
161     checkbox.set_label(label)
162     if box:
163       box.add(checkbox)
164     return checkbox
165
166
167   # -----------------------------------------------------------------------
168   def new_indent(self, box):
169     outer = gtk.HBox()
170     indent = gtk.VBox()
171     outer.pack_start(indent, padding=48)
172     box.add(outer)
173     return indent
174
175  
176   # -----------------------------------------------------------------------
177   def new_input(self, text, box = None):
178     input = hildon.Entry(gtk.HILDON_SIZE_FINGER_HEIGHT)
179     input.set_property('hildon-input-mode', gtk.HILDON_GTK_INPUT_MODE_FULL)
180     input.set_placeholder(text)
181     input.set_property('is-focus', False)
182     if box:
183       box.add(input)
184     return input
185
186
187   # -----------------------------------------------------------------------
188   def add_linked(self, box, ctrl, to_add):
189     box.add(to_add)
190     self.sync_edit(ctrl, to_add)
191     ctrl.connect('toggled', self.sync_edit, to_add)
192     return to_add
193   
194     
195   # -----------------------------------------------------------------------
196   def sync_edit(self, use_twitter, edit):
197     edit.set_property('sensitive', use_twitter.get_active())
198
199
200   # -----------------------------------------------------------------------
201   def need_auth(self, main = False):
202     if main:
203       hildon.hildon_banner_show_information(self.window, '', "Need to authenticate with Facebook")
204     else:
205       gobject.idle_add(self.need_auth, True)
206     
207
208   # -----------------------------------------------------------------------
209   def block_for_auth(self, main = False, lock = None):
210     if main:
211       note = gtk.Dialog('Facebook authorisation', self.window)
212       note.add_button("Validate", gtk.RESPONSE_OK)
213       note.vbox.add(gtk.Label("\nPress 'Validate' once Facebook has\nbeen authenticated in web browser.\n"))
214
215       note.show_all()
216       result = note.run()
217       note.hide()
218       lock.release()
219       
220     else:
221       time.sleep(2)
222       lock = thread.allocate_lock()
223       lock.acquire()
224       gobject.idle_add(self.block_for_auth, True, lock)
225       lock.acquire()
226       lock.release()
227
228
229   # -----------------------------------------------------------------------
230   def progress(self, i, j, main = False):
231     if main:
232       if i == 0:
233         self.progressbar = gtk.ProgressBar()
234         self.progressnote = gtk.Dialog("Fetching friends' info", self.window)
235         self.progressnote.vbox.add(self.progressbar)
236         hildon.hildon_gtk_window_set_progress_indicator(self.progressnote, 1)
237         
238         self.progressnote.show_all()
239         
240       elif i < j:
241         if i == 1:
242           self.progressnote.set_title("Updating contacts")
243           hildon.hildon_gtk_window_set_progress_indicator(self.progressnote, 0)
244           
245         self.progressbar.set_fraction(float(i) / float(j))
246         
247       else:
248         self.progressnote.destroy()
249         
250       print i,j
251     else:
252       gobject.idle_add(self.progress, i, j, True)
253
254
255   # -----------------------------------------------------------------------
256   def report_error(self, e, prefs = False):
257     if self.progressnote:
258       self.window.set_property('sensitive', True)
259       self.progressnote.destroy()
260
261     hildon.hildon_banner_show_information(self.window, '', e)
262     if prefs:
263       self.open_prefs()
264
265
266   # -----------------------------------------------------------------------
267   def take_screenshot(self, event = None, data = None):
268     self.window.disconnect(self.expose_hid)
269     if not os.path.isfile("/home/user/.cache/launch/org.maemo.hermes.pvr"):
270       gobject.timeout_add(80, hildon.hildon_gtk_window_take_screenshot, self.window, True)
271
272
273   # -----------------------------------------------------------------------
274   def main(self):
275     # -- Window and app...
276     #
277     self.app = hildon.Program()
278     self.window = hildon.Window()
279     gtk.set_application_name('Hermes')
280     self.osso_context = osso.Context('org.maemo.hermes', '0.0.5', False)
281     self.app.add_window(self.window)
282
283     self.window.connect("delete-event", gtk.main_quit)
284     self.expose_hid = self.window.connect('expose-event', self.take_screenshot)
285
286     # -- Main window buttons...
287     #
288     self.background, mask = gtk.gdk.pixbuf_new_from_file('/opt/hermes/share/background.png').render_pixmap_and_mask()
289     self.window.realize()
290     self.window.window.set_back_pixmap(self.background, False)
291     alignment = gtk.Alignment(xalign=0.5, yalign=0.8, xscale=0.8)
292     box = gtk.HButtonBox()
293     alignment.add(box)
294     self.window.add(alignment)
295     
296     box.set_property('layout-style', gtk.BUTTONBOX_SPREAD)
297     button = hildon.Button(gtk.HILDON_SIZE_THUMB_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL,
298                            title = 'Retrieve', value = "Get contacts' missing info")
299     button.set_property('width-request', 250)
300     button.connect('clicked', self.doSync, False)
301     box.add(button)
302
303     button = hildon.Button(gtk.HILDON_SIZE_THUMB_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL,
304                            title = 'Update', value = "Update contacts' info")
305     button.set_property('width-request', 250)
306     button.connect('clicked', self.doSync, True)
307     box.add(button)
308
309     # -- Application menu...
310     #
311     menu = hildon.AppMenu()
312     button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
313     button.set_label("Accounts")
314     button.connect("clicked", self.open_prefs)
315     menu.append(button)
316     menu.show_all()
317     self.window.set_app_menu(menu)
318   
319     self.window.show_all()
320     gtk.main()
321
322
323   def get_use_facebook(self):
324     return self.gc.get_bool("/apps/maemo/hermes/use_facebook")
325
326
327   def set_use_facebook(self, value):
328     self.gc.set_bool("/apps/maemo/hermes/use_facebook", value)
329
330   def get_create_empty(self):
331     return self.gc.get_bool("/apps/maemo/hermes/create_empty")
332
333
334   def set_create_empty(self, value):
335     self.gc.set_bool("/apps/maemo/hermes/create_empty", value)
336
337
338   def get_use_twitter(self):
339     return self.gc.get_bool("/apps/maemo/hermes/use_twitter")
340
341
342   def set_use_twitter(self, value, user, password):
343     self.gc.set_bool("/apps/maemo/hermes/use_twitter", value)
344     self.gc.set_string("/apps/maemo/hermes/twitter_user", user)
345     self.gc.set_string("/apps/maemo/hermes/twitter_pwd", password)
346     
347   
348   def get_twitter_credentials(self):
349     return (self.gc.get_string("/apps/maemo/hermes/twitter_user") or '',
350             self.gc.get_string("/apps/maemo/hermes/twitter_pwd") or '')
351
352
353 # -------------------------------------------------------------------------
354 if __name__ == '__main__':
355   HermesGUI().main()
356