Make build system update version number, so it doesn't need to be maintained separately.
[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.2', 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       
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       
206         result = dialog.run()
207         dialog.hide()
208         if result == gtk.RESPONSE_OK:
209             friend = view.get_selected_friend()
210             if friend:
211                 if friend.get_contact() == contact:
212                     hildon.hildon_banner_show_information(self.main_window, '', _("Removing existing mappings is not yet supported"))
213                 elif view.contact_mapped:
214                     fb2c.update_contact(contact, friend, True, True)
215                     widget.add_mapping(friend.get_source())
216                 else:
217                     fb2c.update_contact(contact, friend, False, True)
218                     widget.add_mapping(friend.get_source())
219     
220                         
221     # -----------------------------------------------------------------------
222     def need_auth(self, main = False):
223         """Part of the GUI callback API."""
224         
225         if main:
226             hildon.hildon_banner_show_information(self.main_window, '', _("Need to authenticate via browser"))
227         else:
228             gobject.idle_add(self.need_auth, True)
229       
230     
231     # -----------------------------------------------------------------------
232     def block_for_auth(self, prompt = False, main = False, lock = None):
233         """Part of the GUI callback API."""
234
235         if main:
236             note = gtk.Dialog(_('Service authorisation'), self.main_window)
237             note.add_button(_("Validate"), gtk.RESPONSE_OK)
238             if prompt:
239                 input = hildon.Entry(gtk.HILDON_SIZE_FINGER_HEIGHT)
240                 input.set_property('is-focus', False)
241                 note.set_title(_("Verification code from web browser"))
242                 note.vbox.add(input)
243             else:
244                 note.vbox.add(gtk.Label(_("\nPress 'Validate' once account has\nbeen authorised in web browser.\n")))
245     
246             note.show_all()
247             result = note.run()
248             note.hide()
249             lock.release()
250             if prompt and result == gtk.RESPONSE_OK:
251                 print input.get_text()
252                 return input.get_text()
253             else:
254                 return None
255         
256         else:
257             time.sleep(2)
258             lock = thread.allocate_lock()
259             lock.acquire()
260             gobject.idle_add(self.block_for_auth, prompt, True, lock)
261             lock.acquire()
262             lock.release()
263                       
264                         
265     # -----------------------------------------------------------------------
266     def progress(self, msg, i, j, main = False):
267         """Part of the GUI callback API."""
268
269         if main:
270             if i == 0:
271                 self.progressbar = gtk.ProgressBar()
272                 self.progressnote = gtk.Dialog(_("Initialising connections"), self.main_window)
273                 self.progressnote.vbox.add(self.progressbar)
274                 hildon.hildon_gtk_window_set_progress_indicator(self.progressnote, 1)
275                
276                 self.progressnote.show_all()
277               
278             elif i < j:
279                 self.progressnote.set_title(_(msg))
280                 hildon.hildon_gtk_window_set_progress_indicator(self.progressnote, 0)
281                 
282                 self.progressbar.set_fraction(float(i) / float(j))
283               
284             else:
285                 self.progressnote.destroy()
286               
287             print msg, i,j
288         else:
289             gobject.idle_add(self.progress, msg, i, j, True)
290
291        
292     # -----------------------------------------------------------------------
293     def report_error(self, e, prefs = False):
294         if self.progressnote:
295             self.main_window.set_property('sensitive', True)
296             self.progressnote.destroy()
297     
298         hildon.hildon_banner_show_information(self.main_window, '', e)
299         if prefs:
300             self.do_accounts()