f9484538e928141f044e52a0c846d1263a639c59
[hermes] / package / src / org / maemo / hermes / gui / gtkui.py
1 import gettext
2 import gtk, gobject
3 import traceback
4 import time
5 import thread
6 import urllib2
7 import hildon
8 import conic
9 import webbrowser
10 from org.bleb.wimpworks import WimpWorks
11 from org.maemo.hermes.gui.contactview import ContactView
12 from org.maemo.hermes.gui.mapcontact import MapContact
13 from org.maemo.hermes.gui.accountsdialogue import AccountsDialogue
14 from org.bleb.wimpworks import HildonMainScreenLayout
15 from org.maemo.hermes.engine.hermes import Hermes
16
17 class HermesGUI(WimpWorks):
18     """Provides the GUI for Hermes, allowing the syncing of Facebook and
19        Twitter friends' information with the Evolution contacts' database.
20        
21        Copyright (c) Andrew Flegg <andrew@bleb.org> 2009.
22        Released under the Artistic Licence."""
23
24
25     # -----------------------------------------------------------------------
26     def __init__(self, providers = None):
27         gettext.install('hermes','/opt/hermes/share/locale/')
28         WimpWorks.__init__(self, 'Hermes', version = '0.8.7', dbus_name = 'org.maemo.hermes')
29         self.set_background('background.png')
30         
31         layout = HildonMainScreenLayout(offset = 0.8, container = self)
32         layout.add_button('Retrieve', _("Get contacts' missing info"))
33         layout.add_button('Refresh', _("Update contacts' info"))
34         
35         self.add_menu_action("Accounts")
36         self.add_menu_action("About")
37         self.menu.show_all()
38         
39         self.providers = providers
40         self.progressnote = None
41         connection = conic.Connection()
42         gobject.timeout_add(100, connection.request_connection, conic.CONNECT_FLAG_NONE)
43
44   
45     # -----------------------------------------------------------------------
46     def do_retrieve(self, widget):
47         self.sync(widget, False)
48     
49     
50     # -----------------------------------------------------------------------
51     def do_refresh(self, widget):
52         self.sync(widget, True)
53
54
55     # -----------------------------------------------------------------------
56     def do_accounts(self, widget = None):
57         dialog = AccountsDialogue(self.main_window, self.providers)
58         dialog.show()
59
60
61     # -----------------------------------------------------------------------
62     def do_about(self, widget):
63         """Inspired by HeAboutDialog Python port:
64            http://wiki.maemo.org/Hildon-Extras#HeAboutDialog"""
65
66         dlg = gtk.Dialog(_("About"), self.main_window)
67
68         icon = gtk.Image()
69         icon.set_from_icon_name(self.name.lower(), gtk.ICON_SIZE_DIALOG)
70         icon.set_padding(5, 5)
71
72         name = gtk.Label(self.name)
73         name.set_alignment(0, 1)
74         hildon.hildon_helper_set_logical_font(name, 'X-LargeSystemFont')
75
76         version = gtk.Label('v%s' % (self.version))
77         version.set_alignment(0, 1)
78         version.set_padding(10, 0)
79         hildon.hildon_helper_set_logical_font(version, 'LargeSystemFont')
80         
81         desc = gtk.Label(_("Enrich contacts' from social networks."))
82         desc.set_alignment(0, 0)
83
84         copy = gtk.Label("Copyright (c) 2010 Andrew Flegg, Fredrik Wendt, Tim Samoff")
85         copy.set_alignment(0, 1)
86         copy.set_padding(0, 5)
87         hildon.hildon_helper_set_logical_font(copy, 'SmallSystemFont')
88         hildon.hildon_helper_set_logical_color(copy, gtk.RC_FG, gtk.STATE_NORMAL, "SecondaryTextColor")
89
90         layout = gtk.Table(3, 3, False)
91         layout.attach(icon, 0, 1, 0, 2, 0, gtk.EXPAND, 0, 0)
92         layout.attach(name, 1, 2, 0, 1, 0, gtk.EXPAND | gtk.FILL, 0, 0)
93         layout.attach(version, 2, 3, 0, 1, gtk.EXPAND | gtk.FILL, gtk.EXPAND | gtk.FILL, 0, 0)
94         layout.attach(desc, 1, 3, 1, 2, gtk.EXPAND | gtk.FILL, gtk.EXPAND | gtk.FILL, 0, 0)
95         layout.attach(copy, 0, 3, 2, 3, gtk.EXPAND | gtk.FILL, gtk.EXPAND | gtk.FILL, 0, 0)
96         dlg.vbox.add(layout)
97
98         dlg.add_button(_('Visit website'), gtk.RESPONSE_APPLY)
99         dlg.add_button(_('Report bug'), gtk.RESPONSE_NO)
100         dlg.show_all()
101         response = dlg.run()
102         if response == gtk.RESPONSE_APPLY:
103             webbrowser.open('http://hermes.garage.maemo.org/')
104         elif response == gtk.RESPONSE_NO:
105             webbrowser.open('https://bugs.maemo.org/enter_bug.cgi?product=Hermes')
106         dlg.hide()
107    
108
109     # -----------------------------------------------------------------------
110     def sync(self, widget, force, main = True):
111         enabled = []
112         for provider in self.providers:
113             if self.gconf.get_bool('/apps/maemo/hermes/use_%s' % (provider.get_id())):
114                 enabled.append(provider)
115                 
116         if main and len(enabled) == 0:
117             saved = self.do_accounts()
118             if saved == gtk.RESPONSE_DELETE_EVENT:
119                 return
120             
121         print "doing sync", main
122         
123         if main:
124             self.main_window.set_property('sensitive', False)
125             thread.start_new_thread(self.sync, (widget, force, False))
126         else:
127             try:
128                 self.progress('', 0, 0)
129                 services = []
130                 for provider in enabled:
131                     services.append(provider.service(self))
132                     
133                 hermes = Hermes(services, self.progress)
134                 hermes.run(force)
135                 gobject.idle_add(self.open_summary, hermes)
136         
137             except urllib2.HTTPError, e:
138                 traceback.print_exc()
139                 if e.code == 401:
140                     gobject.idle_add(self.report_error, _('Authentication problem. Check credentials.'), True)
141                 else:
142                     gobject.idle_add(self.report_error, _('Network connection error. Check connectivity.'))
143         
144             except urllib2.URLError, e:
145                 traceback.print_exc()
146                 gobject.idle_add(self.report_error, _('Network connection error. Check connectivity.'))
147           
148             except Exception, e:
149                 traceback.print_exc()
150                 gobject.idle_add(self.report_error, _('Something went wrong: ') + e.message)
151     
152     
153     # -----------------------------------------------------------------------
154     def open_summary(self, fb2c):
155         gobject.idle_add(self.main_window.set_property, 'sensitive', True)
156     
157         dialog = gtk.Dialog(_('Summary'), self.main_window)
158         dialog.add_button(_('Done'), gtk.RESPONSE_OK)
159       
160         button = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL,
161                                title = _('Updated %d contacts') % (len(fb2c.updated)))
162         button.connect('clicked', self.show_contacts, fb2c, fb2c.updated)
163         button.set_property('sensitive', len(fb2c.updated) > 0)
164         dialog.vbox.add(button)
165         
166         button = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL,
167                                title = _('Matched %d contacts') % (len(fb2c.matched)))
168         button.connect('clicked', self.show_contacts, fb2c, fb2c.matched)
169         button.set_property('sensitive', len(fb2c.matched) > 0)
170         dialog.vbox.add(button)
171       
172         button = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL,
173                                title = _('%d contacts unmatched') % (len(fb2c.unmatched)))
174         button.connect('clicked', self.show_contacts, fb2c, fb2c.unmatched)
175         button.set_property('sensitive', len(fb2c.unmatched) > 0)
176         dialog.vbox.add(button)
177       
178         dialog.show_all()
179         dialog.run()
180         dialog.hide()
181     
182     
183     # -----------------------------------------------------------------------
184     def show_contacts(self, widget, fb2c, contacts):
185         view = ContactView(contacts)
186     
187         dialog = gtk.Dialog(_('Contacts'), self.main_window)
188         view.connect('contact-activated', self.map_contact, fb2c)
189         dialog.vbox.add(view)
190         dialog.show_all()
191         dialog.vbox.add(view.get_search())
192       
193         dialog.run()
194         dialog.hide()
195       
196       
197     # -----------------------------------------------------------------------
198     def map_contact(self, widget, contact, fb2c):
199         view = MapContact(fb2c.friends, contact)
200
201         dialog = gtk.Dialog(contact.get_name(), self.main_window)
202         dialog.add_button(_('Update'), gtk.RESPONSE_OK)
203         dialog.vbox.add(view)
204         dialog.show_all()
205         dialog.vbox.add(view.get_search())
206       
207         result = dialog.run()
208         dialog.hide()
209         if result == gtk.RESPONSE_OK:
210             friend = view.get_selected_friend()
211             if friend:
212                 if friend.get_contact() == contact:
213                     hildon.hildon_banner_show_information(self.main_window, '', _("Removing existing mappings is not yet supported"))
214                 elif view.contact_mapped:
215                     fb2c.update_contact(contact, friend, True, True)
216                     widget.add_mapping(friend.get_source())
217                 else:
218                     fb2c.update_contact(contact, friend, False, True)
219                     widget.add_mapping(friend.get_source())
220     
221                         
222     # -----------------------------------------------------------------------
223     def need_auth(self, main = False):
224         """Part of the GUI callback API."""
225         
226         if main:
227             hildon.hildon_banner_show_information(self.main_window, '', _("Need to authenticate via browser"))
228         else:
229             gobject.idle_add(self.need_auth, True)
230       
231     
232     # -----------------------------------------------------------------------
233     def block_for_auth(self, prompt = False, main = False, lock = None):
234         """Part of the GUI callback API."""
235
236         if main:
237             note = gtk.Dialog(_('Service authorisation'), self.main_window)
238             note.add_button(_("Validate"), gtk.RESPONSE_OK)
239             if prompt:
240                 input = hildon.Entry(gtk.HILDON_SIZE_FINGER_HEIGHT)
241                 input.set_property('is-focus', False)
242                 note.set_title(_("Verification code from web browser"))
243                 note.vbox.add(input)
244             else:
245                 note.vbox.add(gtk.Label(_("\nPress 'Validate' once account has\nbeen authorised in web browser.\n")))
246     
247             note.show_all()
248             result = note.run()
249             note.hide()
250             lock.release()
251             if prompt and result == gtk.RESPONSE_OK:
252                 print input.get_text()
253                 return input.get_text()
254             else:
255                 return None
256         
257         else:
258             time.sleep(2)
259             lock = thread.allocate_lock()
260             lock.acquire()
261             gobject.idle_add(self.block_for_auth, prompt, True, lock)
262             lock.acquire()
263             lock.release()
264                       
265                         
266     # -----------------------------------------------------------------------
267     def progress(self, msg, i, j, main = False):
268         """Part of the GUI callback API."""
269
270         if main:
271             if i == 0:
272                 self.progressbar = gtk.ProgressBar()
273                 self.progressnote = gtk.Dialog(_("Initialising connections"), self.main_window)
274                 self.progressnote.vbox.add(self.progressbar)
275                 hildon.hildon_gtk_window_set_progress_indicator(self.progressnote, 1)
276                
277                 self.progressnote.show_all()
278               
279             elif i < j:
280                 self.progressnote.set_title(_(msg))
281                 hildon.hildon_gtk_window_set_progress_indicator(self.progressnote, 0)
282                 
283                 self.progressbar.set_fraction(float(i) / float(j))
284               
285             else:
286                 self.progressnote.destroy()
287               
288             print msg, i,j
289         else:
290             gobject.idle_add(self.progress, msg, i, j, True)
291
292        
293     # -----------------------------------------------------------------------
294     def report_error(self, e, prefs = False):
295         if self.progressnote:
296             self.main_window.set_property('sensitive', True)
297             self.progressnote.destroy()
298     
299         hildon.hildon_banner_show_information(self.main_window, '', e)
300         if prefs:
301             self.do_accounts()