initial commit
[fmms] / src / fmms_sender_ui.py
1 #!/usr/bin/env python2.5
2 # -*- coding: utf-8 -*-
3 """ Sender 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 import socket
11 import re
12 import Image
13 import mimetypes
14
15 import gtk
16 import hildon
17 import gobject
18 import osso
19 import dbus
20
21 from wappushhandler import MMSSender
22 import fmms_config as fMMSconf
23 import contacts as ContactH
24
25
26
27 class fMMS_GUI(hildon.Program):
28         def __init__(self, spawner=None):
29                 hildon.Program.__init__(self)
30                 program = hildon.Program.get_instance()
31                 
32                 self.config = fMMSconf.fMMS_config()
33                 self.ch = ContactH.ContactHandler()
34                 
35                 self.window = hildon.StackableWindow()
36                 self.window.set_title("fMMS - New MMS")
37                 program.add_window(self.window)
38                 
39                 self.window.connect("delete_event", self.quit)
40                 
41                 if spawner != None:
42                         self.spawner = spawner
43                 else:
44                         self.spawner = self.window
45                 allBox = gtk.VBox()
46                 
47                 """ Begin top section """
48                 topHBox1 = gtk.HBox()
49                 
50                 bTo = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_HORIZONTAL, "     To     ")
51                 bTo.connect('clicked', self.open_contacts_dialog)
52                 self.eNumber = hildon.Entry(gtk.HILDON_SIZE_FINGER_HEIGHT)
53                 
54                 #topHBox.add(bTo)
55                 #topHBox.add(eNumber)
56                 topHBox1.pack_start(bTo, False, True, 0)
57                 topHBox1.pack_start(self.eNumber, True, True, 0)
58                 
59                 
60                 """ Begin midsection """
61                 pan = hildon.PannableArea()
62                 pan.set_property("mov-mode", hildon.MOVEMENT_MODE_BOTH)         
63                 
64                 #midHBox = gtk.HBox()
65                 self.tvMessage = hildon.TextView()
66                 self.tvMessage.set_wrap_mode(gtk.WRAP_WORD)
67                 
68                 #midHBox.pack_start(self.tvMessage, True, True, 0)
69                 pan.add_with_viewport(self.tvMessage)
70                 
71                 """ Begin botsection """
72                 
73                 botHBox = gtk.HBox()
74                 #self.bAttachment = gtk.FileChooserButton('')
75                 #self.bAttachment.connect('file-set', self.update_size)
76                 self.bAttachment = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_HORIZONTAL, "Attachment")
77                 self.bAttachment.connect('clicked', self.open_file_dialog)
78                 
79                 self.lSize = gtk.Label('')
80                 
81                 self.bSend = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_HORIZONTAL, "    Send    ")
82                 self.bSend.connect('clicked', self.send_mms)
83                 
84                 botHBox.pack_start(self.bAttachment)
85                 botHBox.pack_start(self.lSize)
86                 botHBox.pack_end(self.bSend, False, False, 5)
87                 
88
89                 """ Show it all! """
90                 allBox.pack_start(topHBox1, False, False)
91                 #allBox.pack_start(topHBox2, False, False)
92                 #allBox.pack_start(midHBox, True, True)
93                 allBox.pack_start(pan, True, True)
94                 allBox.pack_start(botHBox, False, False)
95                 
96                 #self.pan = pan
97                 #self.pan.add_with_viewport(allBox)
98                 #self.window.add(self.pan)
99                 self.window.add(allBox)
100                 self.window.show_all()
101                 self.add_window(self.window)
102         
103         # TODO: pass reference instead of making it available in the object?
104         def open_contacts_dialog(self, button):
105                 selector = self.create_contacts_selector()
106                 self.contacts_dialog = gtk.Dialog("Select a contact")
107
108                 # TODO: remove hardcoded height
109                 self.contacts_dialog.set_default_size(-1, 320)
110                                                             
111                 self.contacts_dialog.vbox.pack_start(selector)
112                 self.contacts_dialog.add_button("Done", 1)
113                 self.contacts_dialog.show_all()
114                 while 1:
115                         ret = self.contacts_dialog.run()
116                         if ret == 1:
117                                 ret2 = self.contact_selector_changed(selector)
118                                 if ret2 == 0:
119                                         break
120                         else:
121                                 break
122                 self.contacts_dialog.destroy()
123
124         """ forces ui update, kinda... god this is AWESOME """
125         def force_ui_update(self):
126                 while gtk.events_pending():
127                         gtk.main_iteration(False)
128         
129         def contact_number_chosen(self, button, nrdialog):
130                 print button.get_label()
131                 nr = button.get_label().replace(" ", "")
132                 nr = re.sub("[^0-9]\+", "", nr)
133                 self.eNumber.set_text(nr)
134                 nrdialog.response(0)
135                 self.contacts_dialog.response(0)
136                 
137         def contact_selector_changed(self, selector):
138                 username = selector.get_current_text()
139                 nrlist = self.ch.get_numbers_from_name(username)
140                 print nrlist
141                 nrdialog = gtk.Dialog("Pick a number")
142                 for number in nrlist:
143                         print number
144                         numberbox = gtk.HBox()
145                         typelabel = gtk.Label(nrlist[number].capitalize())
146                         typelabel.set_width_chars(24)
147                         button = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_HORIZONTAL)
148                         button.set_label(number)
149                         button.connect('clicked', self.contact_number_chosen, nrdialog)
150                         numberbox.pack_start(typelabel, False, False, 0)
151                         numberbox.pack_start(button, True, True, 0)
152                         nrdialog.vbox.pack_start(numberbox)
153                 nrdialog.show_all()
154                 # this is blocking until we get a return
155                 ret = nrdialog.run()
156                 print "changed ret:", ret
157                 nrdialog.destroy()
158                 return ret
159         
160         def create_contacts_selector(self):
161                 #Create a HildonTouchSelector with a single text column
162                 selector = hildon.TouchSelectorEntry(text = True)
163                 #selector.connect('changed', self.contact_selector_changed)
164
165                 cl = self.ch.get_contacts_as_list()
166
167                 # Populate selector
168                 for contact in cl:
169                         if contact != None:
170                                 # Add item to the column 
171                                 #print "adding", contact
172                                 selector.append_text(contact)
173
174                 # Set selection mode to allow multiple selection
175                 selector.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
176                 return selector
177
178                 
179         def open_file_dialog(self, button):
180                 #fsm = hildon.FileSystemModel()
181                 #fcd = hildon.FileChooserDialog(self.window, gtk.FILE_CHOOSER_ACTION_OPEN, fsm)
182                 # this shouldnt issue a warning according to the pymaemo mailing list, but does
183                 # anyway, nfc why :(
184                 fcd = gobject.new(hildon.FileChooserDialog, action=gtk.FILE_CHOOSER_ACTION_OPEN)
185                 fcd.set_default_response(gtk.RESPONSE_OK)
186                 ret = fcd.run()
187                 if ret == gtk.RESPONSE_OK:
188                         ### filesize check
189                         ### TODO: dont hardcode
190                         filesize = os.path.getsize(fcd.get_filename()) / 1024
191                         if filesize > 10240:
192                                 banner = hildon.hildon_banner_show_information(self.window, "", "10MB attachment limit in effect, please try another file")
193                                 self.bAttachment.set_label("Attachment")
194                         else:
195                                 self.bAttachment.set_label(fcd.get_filename())
196                                 self.update_size(fcd.get_filename())
197                         fcd.destroy()
198                 else:
199                         fcd.destroy()
200         
201         """ resize an image """
202         """ thanks tomaszf for this function """
203         """ slightly modified by frals """
204         def resize_img(self, filename):
205                 try:
206                         if not os.path.isdir(self.config.get_imgdir()):
207                                 print "creating dir", self.config.get_imgdir()
208                                 os.makedirs(self.config.get_imgdir())
209                         
210                         hildon.hildon_banner_show_information(self.window, "", "fMMS: Resizing image, this might take a while...")
211                         self.force_ui_update()
212                         
213                         img = Image.open(filename)
214                         print "height", img.size[1]
215                         print "width", img.size[0]
216                         newWidth = int(self.config.get_img_resize_width())
217                         if img.size[0] > newWidth:
218                                 print "resizing"
219                                 newWidth = int(self.config.get_img_resize_width())
220                                 newHeight = int(newWidth * img.size[1] / img.size[0])
221                                 print "Resizing image:", str(newWidth), "*", str(newHeight)
222
223                                 # Image.BILINEAR, Image.BICUBIC, Image.ANTIALIASING
224                                 rimg = img.resize((newWidth, newHeight), Image.BILINEAR)
225                                 filename = filename.rpartition("/")
226                                 filename = filename[-1]
227                                 rattachment = self.config.get_imgdir() + filename
228                                 rimg.save(rattachment)
229                                 self.attachmentIsResized = True
230                         else:
231                                 print "not resizing"
232                                 rattachment = filename
233                                 
234                         return rattachment
235                 
236                 except Exception, e:
237                         print "resizing failed:", e, e.args
238                         raise
239         
240         """ sends the message (no shit?) """
241         def send_mms(self, widget):
242                 hildon.hildon_gtk_window_set_progress_indicator(self.window, 1)
243                 # Disable send-button
244                 self.bSend.set_sensitive(False)
245                 self.force_ui_update()
246                 
247                 self.osso_c = osso.Context("fMMS", "0.1.0", False)
248                 
249                 attachment = self.bAttachment.get_label()
250                 if attachment == "Attachment" or attachment == None:
251                         attachment = None
252                         self.attachmentIsResized = False
253                 else:
254                         print attachment
255                         filetype = mimetypes.guess_type(attachment)[0]
256                         print self.config.get_img_resize_width()
257                         self.attachmentIsResized = False
258                         print filetype.startswith("image")
259                         if self.config.get_img_resize_width() != 0 and filetype.startswith("image"):
260                                 try:
261                                         attachment = self.resize_img(attachment)
262                                 except Exception, e:
263                                         print e, e.args
264                                         note = osso.SystemNote(self.osso_c)
265                                         errmsg = str(e.args)
266                                         note.system_note_dialog("Resizing failed:\nError: " + errmsg , 'notice')
267                                         raise
268                 
269                 to = self.eNumber.get_text()
270                 sender = self.config.get_phonenumber()
271                 tb = self.tvMessage.get_buffer()
272                 message = tb.get_text(tb.get_start_iter(), tb.get_end_iter())
273                 print sender, attachment, to, message
274
275                 """ Construct and send the message, off you go! """
276                 # TODO: remove hardcoded subject
277                 try:
278                         sender = MMSSender(to, "MMS", message, attachment, sender)
279                         (status, reason, output) = sender.sendMMS()
280                         ### TODO: Clean up and make this look decent
281                         message = str(status) + "_" + str(reason)
282                 
283                         reply = str(output)
284                         #print message
285                         #note = osso.SystemNote(self.osso_c)
286                         #ret = note.system_note_dialog("MMSC REPLIED:" + message + "\nBODY:" + reply, 'notice')
287                         banner = hildon.hildon_banner_show_information(self.window, "", "MMSC REPLIED:" + message + "\nBODY: " + reply)
288                         
289                 except TypeError, exc:
290                         print type(exc), exc
291                         note = osso.SystemNote(self.osso_c)
292                         errmsg = "Invalid attachment"
293                         note.system_note_dialog("Sending failed:\nError: " + errmsg + " \nPlease make sure the file is valid" , 'notice')
294                         #raise
295                 except socket.error, exc:
296                         print type(exc), exc
297                         code = str(exc.args[0])
298                         text = str(exc.args[1])
299                         note = osso.SystemNote(self.osso_c)
300                         errmsg = code + " " + text
301                         note.system_note_dialog("Sending failed:\nError: " + errmsg + " \nPlease make sure APN settings are correct" , 'notice')
302                         #raise
303                 except Exception, exc:
304                         print type(exc)
305                         print exc
306                         raise
307                 finally:
308                         hildon.hildon_gtk_window_set_progress_indicator(self.window, 0)
309                         self.bSend.set_sensitive(True)
310                         
311                 if self.attachmentIsResized == True:
312                         print "Removing temporary image..."
313                         os.remove(attachment)
314                 #self.window.destroy()
315                 
316         def update_size(self, fname):
317                 try:
318                         size = os.path.getsize(fname) / 1024
319                         self.lSize.set_markup("Size:\n<small>" + str(size) + "kB</small>")      
320                 except TypeError:
321                         self.lSize.set_markup("")
322
323         def quit(self, *args):
324                 gtk.main_quit()
325
326         def run(self):
327                 self.window.show_all()
328                 gtk.main()
329                 
330 if __name__ == "__main__":
331         app = fMMS_GUI()
332         app.run()