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