initial commit
[fmms] / src / fmms_gui.py
1 #!/usr/bin/env python2.5
2 # -*- coding: utf-8 -*-
3 """ Main-view UI for fMMS
4
5 @author: Nick Leppänen Larsson <frals@frals.se>
6 @license: GNU GPL
7 """
8 import os
9 import time
10
11 import gtk
12 import hildon
13 import osso
14 import gobject
15 import dbus
16 from gnome import gnomevfs
17
18 from wappushhandler import PushHandler
19 import fmms_config as fMMSconf
20 import fmms_sender_ui as fMMSSenderUI
21 import fmms_viewer as fMMSViewer
22 import controller as fMMSController
23 import contacts as ContactH
24
25 class fMMS_GUI(hildon.Program):
26
27         def __init__(self):
28                 self.cont = fMMSController.fMMS_controller()
29                 self.config = fMMSconf.fMMS_config()
30                 self._mmsdir = self.config.get_mmsdir()
31                 self._pushdir = self.config.get_pushdir()
32                 self.ch = ContactH.ContactHandler()
33                 self.osso_c = osso.Context("fMMS", "0.1.0", False)
34         
35                 if not os.path.isdir(self._mmsdir):
36                         print "creating dir", self._mmsdir
37                         os.makedirs(self._mmsdir)
38                 if not os.path.isdir(self._pushdir):
39                         print "creating dir", self._pushdir
40                         os.makedirs(self._pushdir)
41         
42                 hildon.Program.__init__(self)
43                 program = hildon.Program.get_instance()
44                 
45                 self.osso_rpc = osso.Rpc(self.osso_c)
46                 self.osso_rpc.set_rpc_callback("se.frals.fmms","/se/frals/fmms","se.frals.fmms", self.cb_open_fmms, self.osso_c)
47                 
48                 self.window = hildon.StackableWindow()
49                 self.window.set_title("fMMS")
50                 program.add_window(self.window)
51                 
52                 self.window.connect("delete_event", self.quit)
53                 
54                 pan = hildon.PannableArea()
55                 pan.set_property("mov-mode", hildon.MOVEMENT_MODE_BOTH)
56                 
57                 
58                 ### TODO: dont hardcode the values here.. oh well
59                 iconcell = gtk.CellRendererPixbuf()
60                 photocell = gtk.CellRendererPixbuf()
61                 textcell = gtk.CellRendererText()
62                 iconcell.set_fixed_size(48, 64)
63                 cell2 = gtk.CellRendererText()
64                 cell2.set_property('xalign', 1.0)
65                 photocell.set_property('xalign', 1.0)
66                 photocell.set_fixed_size(64, 64)
67                 textcell.set_property('mode', gtk.CELL_RENDERER_MODE_INERT)
68                 textcell.set_fixed_size(650, 64)
69                 textcell.set_property('xalign', 0.0)
70                 
71                 self.liststore = gtk.ListStore(gtk.gdk.Pixbuf, str, gtk.gdk.Pixbuf, str)
72                 self.treeview = hildon.GtkTreeView(gtk.HILDON_UI_MODE_EDIT)
73                 self.treeview.set_model(self.liststore)
74
75                 
76                 icon_col = gtk.TreeViewColumn('Icon')
77                 sender_col = gtk.TreeViewColumn('Sender')
78                 placeholder_col = gtk.TreeViewColumn('Photo')
79                 
80                 
81                 self.add_buttons_liststore()
82                 
83                 self.treeview.append_column(icon_col)
84                 self.treeview.append_column(sender_col)
85                 self.treeview.append_column(placeholder_col)
86                 
87                 icon_col.pack_start(iconcell, False)
88                 icon_col.set_attributes(iconcell, pixbuf=0)
89                 sender_col.pack_start(textcell, True)
90                 sender_col.set_attributes(textcell, markup=1)
91                 placeholder_col.pack_end(photocell, False)
92                 placeholder_col.set_attributes(photocell, pixbuf=2)
93                 
94                 selection = self.treeview.get_selection()
95                 #selection.set_mode(gtk.SELECTION_SINGLE)
96                 self.treeview.connect('hildon-row-tapped', self.show_mms)
97                 
98                 
99                 self.liststore_menu = self.liststore_mms_menu()
100                 self.treeview.tap_and_hold_setup(self.liststore_menu)
101                 #treeview.connect('tap-and-hold', self.liststore_mms_clicked)
102                 
103         
104                 pan.add_with_viewport(self.treeview)
105                 self.window.add(pan)
106         
107                 self.menu = self.create_menu()
108                 self.window.set_app_menu(self.menu)
109                 self.window.show_all()
110                 self.add_window(self.window)
111                 
112                 if self.config.get_firstlaunch() == 1:
113                         print "firstlaunch"
114                         note = osso.SystemNote(self.osso_c)
115                         firstlaunchmessage = "NOTE: Currently you have to connect manually to the MMS APN when sending and receiving.\nAlso, only implemented attachment is image."
116                         note.system_note_dialog(firstlaunchmessage , 'notice')
117                         self.create_config_dialog()
118                         self.config.set_firstlaunch(0)
119                 
120         def cb_open_fmms(self, interface, method, args, user_data):
121                 if method != 'open_mms' and method != 'open_gui':
122                         return
123                 if method == 'open_mms':
124                         try:
125                                 checkfile = os.path.isfile(self._pushdir + args[0])
126                                 if checkfile == True:
127                                         filename = args[0]
128                         except:
129                                 return
130                         viewer = fMMSViewer.fMMS_Viewer(filename)
131                 elif method == 'open_gui':
132                         print "open_gui called"
133                         self.liststore.clear()
134                         self.add_buttons_liststore()
135                         return
136                 
137         def create_menu(self):
138                 menu = hildon.AppMenu()
139                 
140                 send = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
141                 send.set_label("New MMS")
142                 send.connect('clicked', self.menu_button_clicked)
143                 
144                 config = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
145                 config.set_label("Configuration")
146                 config.connect('clicked', self.menu_button_clicked)
147                 
148                 about = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
149                 about.set_label("About")
150                 about.connect('clicked', self.menu_button_clicked)
151                 
152                 menu.append(send)
153                 menu.append(config)
154                 menu.append(about)
155                 
156                 menu.show_all()
157                 
158                 return menu
159                 
160         def menu_button_clicked(self, button):
161                 buttontext = button.get_label()
162                 if buttontext == "Configuration":
163                         ret = self.create_config_dialog()
164                 elif buttontext == "New MMS":
165                         ret = fMMSSenderUI.fMMS_GUI(self.window).run()
166                 elif buttontext == "About":
167                         ret = self.create_about_dialog()
168                 
169         def create_about_dialog(self):
170                 dialog = gtk.AboutDialog()                                                 
171                 dialog.set_name("fMMS")
172                 fmms_logo = gtk.gdk.pixbuf_new_from_file("/opt/fmms/fmms.png")
173                 dialog.set_logo(fmms_logo)                                   
174                 dialog.set_comments('MMS send and receive support for Fremantle')                      
175                 dialog.set_version(self.config.get_version())                                                
176                 dialog.set_copyright("By Nick Leppänen Larsson (aka frals)")                    
177                 dialog.set_website("http://mms.frals.se/")                                  
178                 dialog.connect("response", lambda d, r: d.destroy())                      
179                 dialog.show() 
180  
181         def create_config_dialog(self):
182                 dialog = gtk.Dialog()
183                 dialog.set_title("Configuration")
184                 
185                 allVBox = gtk.VBox()
186                 
187                 self.active_apn_index = 0
188                 
189                 apnHBox = gtk.HBox()
190                 apn_label = gtk.Label("APN:")
191                 self.selector = self.create_apn_selector()
192                 self.apn = hildon.PickerButton(gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_HORIZONTAL)
193                 self.apn.set_selector(self.selector)
194                 self.apn.set_active(self.active_apn_index)
195
196                 apnHBox.pack_start(apn_label, False, True, 0)
197                 apnHBox.pack_start(self.apn, True, True, 0)
198                 
199                 mmscHBox = gtk.HBox()
200                 mmsc_label = gtk.Label("MMSC:")
201                 self.mmsc = hildon.Entry(gtk.HILDON_SIZE_FINGER_HEIGHT)
202                 mmsc_text = self.config.get_mmsc()
203                 if mmsc_text != None:   
204                         self.mmsc.set_text(mmsc_text)
205                 else:
206                         self.mmsc.set_text("http://")
207                 mmscHBox.pack_start(mmsc_label, False, True, 0)
208                 mmscHBox.pack_start(self.mmsc, True, True, 0)
209                 
210                 numberHBox = gtk.HBox()
211                 number_label = gtk.Label("Your phonenumber:")
212                 self.number = hildon.Entry(gtk.HILDON_SIZE_FINGER_HEIGHT)
213                 number_text = self.config.get_phonenumber()
214                 if number_text != None:
215                         self.number.set_text(number_text)
216                 else:
217                         self.number.set_text("")
218                 numberHBox.pack_start(number_label, False, True, 0)
219                 numberHBox.pack_start(self.number, True, True, 0)
220                 
221                 imgwidthHBox = gtk.HBox()
222                 imgwidth_label = gtk.Label("Resize image width:")
223                 self.imgwidth = hildon.Entry(gtk.HILDON_SIZE_FINGER_HEIGHT)
224                 imgwidth_text = self.config.get_img_resize_width()
225                 if imgwidth_text != None:
226                         self.imgwidth.set_text(str(imgwidth_text))
227                 else:
228                         self.imgwidth.set_text("")
229                 imgwidthHBox.pack_start(imgwidth_label, False, True, 0)
230                 imgwidthHBox.pack_start(self.imgwidth, True, True, 0)
231                 
232                 notelabel = gtk.Label("APN refers to the name of the connection in\n \"Internet Connections\" to use.")
233                 
234                 allVBox.pack_start(notelabel, False, True, 0)
235                 allVBox.pack_start(apnHBox, False, False, 0)
236                 allVBox.pack_start(mmscHBox, False, False, 0)
237                 allVBox.pack_end(numberHBox, False, False, 0)
238                 allVBox.pack_end(imgwidthHBox, False, False, 0)
239                 
240                 allVBox.show_all()
241                 dialog.vbox.add(allVBox)
242                 dialog.add_button("Save", gtk.RESPONSE_APPLY)
243                 while 1:
244                         ret = dialog.run()
245                         ret2 = self.config_menu_button_clicked(ret)
246                         if ret2 == 0 or ret2 == None: 
247                                 break
248                         
249                 dialog.destroy()
250                 return ret
251
252
253         """ selector for apn """
254         def create_apn_selector(self):
255                 selector = hildon.TouchSelector(text = True)
256                 apnlist = self.config.get_gprs_apns()
257                 currval = self.config.get_apn_nicename()
258                 # Populate selector
259                 i = 0
260                 for apn in apnlist:
261                         if apn != None:
262                                 if apn == currval:
263                                         self.active_apn_index = i
264                                 i += 1  
265                                 # Add item to the column 
266                                 selector.append_text(apn)
267                         
268                 selector.center_on_selected()
269                 selector.set_active(0, i)
270                 # Set selection mode to allow multiple selection
271                 selector.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
272                 return selector
273                 
274                 
275         def config_menu_button_clicked(self, action):
276                 if action == gtk.RESPONSE_APPLY:
277                         print self.apn.get_selector().get_current_text()
278                         ret_setapn = self.config.get_apnid_from_name(self.apn.get_selector().get_current_text())
279                         if ret_setapn != None:
280                                 self.config.set_apn(ret_setapn)
281                                 print "Set apn to: %s" % ret_setapn
282                                 ret = self.config.set_mmsc(self.mmsc.get_text())
283                                 print "Set mmsc to %s" % self.mmsc.get_text()
284                                 self.config.set_phonenumber(self.number.get_text())
285                                 print "Set phonenumber to %s" % self.number.get_text()
286                                 self.config.set_img_resize_width(self.imgwidth.get_text())
287                                 print "Set image width to %s" % self.imgwidth.get_text()
288                                 banner = hildon.hildon_banner_show_information(self.window, "", "Settings saved")
289                                 return 0
290                         else:
291                                 print "Set mmsc to %s" % self.mmsc.get_text()
292                                 self.config.set_phonenumber(self.number.get_text())
293                                 print "Set phonenumber to %s" % self.number.get_text()
294                                 self.config.set_img_resize_width(self.imgwidth.get_text())
295                                 print "Set image width to %s" % self.imgwidth.get_text()
296                                 banner = hildon.hildon_banner_show_information(self.window, "", "Could not save APN settings. Did you enter a correct APN?")
297                                 banner.set_timeout(5000)
298                                 return -1
299                 
300
301         """ add each item to our liststore """
302         def add_buttons_liststore(self):
303                         icon_theme = gtk.icon_theme_get_default()
304                         
305                         pushlist = self.cont.get_push_list()
306                         for varlist in pushlist:
307                                 mtime = varlist['Time']
308                                 fname = varlist['Transaction-Id']
309                                 direction = self.cont.get_direction_mms(fname)
310                                 try:
311                                         sender = varlist['From']
312                                         sender = sender.replace("/TYPE=PLMN", "")
313                                 except:
314                                         sender = "0000000"
315                                 
316                                 if direction == fMMSController.MSG_DIRECTION_OUT:
317                                         sender = "You (Outgoing)"
318                                 
319                                 sendername = self.ch.get_name_from_number(sender)
320                                 photo = icon_theme.load_icon("general_default_avatar", 48, 0)
321                                 if sendername != None:
322                                         sender = sendername + ' <span size="smaller">(' + sender + ')</span>'
323                                         phototest = self.ch.get_photo_from_name(sendername, 64)
324                                         if phototest != None:   
325                                                 photo = phototest
326                                                 #print "loaded photo:", photo.get_width(), photo.get_height()
327         
328                                 #title = sender + " - " + mtime
329                                 
330                                 if self.cont.is_fetched_push_by_transid(fname):
331                                         icon = icon_theme.load_icon("general_sms", 48, 0)
332                                 else:
333                                         icon = icon_theme.load_icon("chat_unread_sms", 48, 0)
334                                 self.liststore.append([icon, sender + '  <span foreground="#666666" size="smaller"><sup>' + mtime + '</sup></span>\n<span foreground="#666666" size="x-small">' + fname + '</span>', photo, fname])
335         
336         """ lets call it quits! """
337         def quit(self, *args):
338                 gtk.main_quit()
339         
340         
341         """ forces ui update, kinda... god this is AWESOME """
342         def force_ui_update(self):
343                 while gtk.events_pending():
344                         gtk.main_iteration(False)
345                 
346                 
347         """ delete push message """
348         def delete_push(self, fname):
349                 self.cont.delete_push_message(fname)
350                 
351         
352         """ delete mms message (eg for redownload) """
353         def delete_mms(self, fname):
354                 self.cont.delete_mms_message(fname)
355         
356         """ delete push & mms """
357         def delete_push_mms(self, fname):
358                 try:
359                         self.cont.wipe_message(fname)
360                         banner = hildon.hildon_banner_show_information(self.window, "", "fMMS: Message deleted")
361                 except Exception, e:
362                         print "Exception caught:"
363                         print type(e), e
364                         raise
365                         banner = hildon.hildon_banner_show_information(self.window, "", "fMMS: Failed to delete message.")
366
367
368         """ action on delete contextmenu click """
369         def liststore_delete_clicked(self, widget):
370                 dialog = gtk.Dialog()
371                 dialog.set_title("Confirm")
372                 dialog.add_button(gtk.STOCK_YES, 1)
373                 dialog.add_button(gtk.STOCK_NO, 0)
374                 label = gtk.Label("Are you sure you want to delete the message?")
375                 dialog.vbox.add(label)
376                 dialog.show_all()
377                 ret = dialog.run()
378                 if ret == 1:
379                         (model, miter) = self.treeview.get_selection().get_selected()
380                         # the 4th value is the filename (start counting at 0)
381                         filename = model.get_value(miter, 3)
382                         print "deleting", filename
383                         self.delete_push_mms(filename)
384                         self.liststore.remove(miter)
385                 dialog.destroy()
386                 return
387         
388         """ action on redl contextmenu click """
389         def liststore_redl_clicked(self, widget):
390                 hildon.hildon_gtk_window_set_progress_indicator(self.window, 1)
391                 dialog = gtk.Dialog()
392                 dialog.set_title("WARNING")
393                 dialog.add_button(gtk.STOCK_YES, 1)
394                 dialog.add_button(gtk.STOCK_NO, 0)
395                 label = gtk.Label("If the message is no longer on your MMSC,\n the message will be lost. Continue?")
396                 dialog.vbox.add(label)
397                 dialog.show_all()
398                 ret = dialog.run()
399                 dialog.destroy()
400                 self.force_ui_update()
401                 
402                 if ret == 1:
403                         (model, miter) = self.treeview.get_selection().get_selected()
404                         # the 4th value is the filename (start counting at 0)
405                         filename = model.get_value(miter, 3)
406                         print "redownloading", filename
407                         try:
408                                 self.delete_mms(filename)
409                                 banner = hildon.hildon_banner_show_information(self.window, "", "fMMS: Trying to download MMS...")
410                                 self.force_ui_update()
411                                 
412                                 # TODO: FIXME
413                                 
414                                 self.cont.get_mms_from_push(filename)
415                                 self.show_mms(self.treeview, model.get_path(miter))
416                         except Exception, e:
417                                 print type(e), e
418                                 #raise
419                                 banner = hildon.hildon_banner_show_information(self.window, "", "fMMS: Operation failed")
420                         hildon.hildon_gtk_window_set_progress_indicator(self.window, 0)
421                 return
422
423         """ long press on image creates this """
424         def liststore_mms_menu(self):
425                 menu = gtk.Menu()
426                 menu.set_title("hildon-context-sensitive-menu")
427
428                 redlItem = gtk.MenuItem("Redownload")
429                 menu.append(redlItem)
430                 redlItem.connect("activate", self.liststore_redl_clicked)
431                 redlItem.show()
432                 
433                 separator = gtk.MenuItem()
434                 menu.append(separator)
435                 separator.show()
436                 
437                 openItem = gtk.MenuItem("Delete")
438                 menu.append(openItem)
439                 openItem.connect("activate", self.liststore_delete_clicked)
440                 openItem.show()
441                 
442                 menu.show_all()
443                 return menu
444
445         """ show the selected mms """           
446         def show_mms(self, treeview, path):
447                 # Show loading indicator
448                 hildon.hildon_gtk_window_set_progress_indicator(self.window, 1)
449                 banner = hildon.hildon_banner_show_information(self.window, "", "fMMS: Opening message")
450                 self.force_ui_update()
451                 
452                 print path
453                 model = treeview.get_model()
454                 miter = model.get_iter(path)
455                 # the 4th value is the transactionid (start counting at 0)
456                 transactionid = model.get_value(miter, 3)
457                 
458                 try:
459                         viewer = fMMSViewer.fMMS_Viewer(transactionid)
460                 except Exception, e:
461                         print type(e), e
462                         #raise
463                 
464                 
465                 hildon.hildon_gtk_window_set_progress_indicator(self.window, 0)
466
467         def run(self):
468                 self.window.show_all()
469                 gtk.main()
470                 
471 if __name__ == "__main__":
472         app = fMMS_GUI()
473         app.run()