1 # Copyright (C) 2009, Justin Lewis (jtl1728@rit.edu)
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 2 of the License, or
6 # (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software
15 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 from gettext import gettext as _
23 from sugar.activity.activity import ActivityToolbox
24 from sugar.graphics.toolbutton import ToolButton
25 from sugar.graphics.objectchooser import ObjectChooser
26 from sugar.graphics.alert import NotifyAlert, Alert
28 from MyExceptions import InShareException, FileUploadFailure, ServerRequestFailure, NoFreeTubes
30 _logger = logging.getLogger('fileshare-activity')
33 def __init__(self, activity, tree, handle):
34 self.activity = activity
39 def requestAddFile(self, widget, data=None):
40 _logger.info('Requesting to add file')
42 chooser = ObjectChooser()
43 if chooser.run() == gtk.RESPONSE_ACCEPT:
44 # get object and build file
45 jobject = chooser.get_selected_object()
47 self.show_throbber(True, _("Packaging Object") )
49 file_obj = self.activity.build_file( jobject )
50 except InShareException:
51 self._alert(_("Object Not Added"), _("Object already shared"))
52 self.show_throbber( False )
55 # No problems continue
56 self.show_throbber( False )
59 self._addFileToUIList( file_obj.id, file_obj )
61 # Register File with activity share list
62 self.activity._registerShareFile( file_obj.id, file_obj )
65 if data and data.has_key('upload'):
66 self.show_throbber(True, _("Uploading Object to server"))
69 self.activity.send_file_to_server( file_obj.id, file_obj )
70 except FileUploadFailure:
71 self._alert( _("Failed to upload object") )
72 self._remFileFromUIList( file_obj.id )
73 self.activity.delete_file( file_obj.id )
74 self.show_throbber( False )
75 threading.Thread(target=send).start()
80 def requestInsFile(self, widget, data=None):
81 _logger.info('Requesting to install file back to journal')
83 model, iterlist = self.treeview.get_selection().get_selected_rows()
85 iter = model.get_iter(path)
86 key = model.get_value(iter, 0)
88 # Attempt to remove file from system
89 bundle_path = os.path.join(self._filepath, '%s.xoj' % key)
91 self.activity._installBundle( bundle_path )
92 self._alert(_("Installed bundle to Jorunal"))
94 def requestRemFile(self, widget, data=None):
95 """Removes file from memory then calls rem file from ui"""
96 _logger.info('Requesting to delete file')
98 model, iterlist = self.treeview.get_selection().get_selected_rows()
100 iter = model.get_iter(path)
101 key = model.get_value(iter, 0)
103 # DO NOT DELETE IF TRANSFER IN PROGRESS/COMPLETE
104 if model.get_value(iter, 1).aquired == 0 or self.activity.server_ui_del_overide():
106 # Remove file from UI
107 self._remFileFromUIList(key)
109 # UnRegister File with activity share list
110 self.activity._unregisterShareFile( key )
112 # Attempt to remove file from system
113 self.activity.delete_file( key )
115 # If added by rem from server button, data will have remove key
116 if data and data.has_key('remove'):
119 self.activity.remove_file_from_server( key )
120 except ServerRequestFailure:
121 self._alert( _("Failed to send remove request to server") )
122 self.show_throbber( False )
123 self.show_throbber(True, _("Sending request to server"))
124 threading.Thread(target=call).start()
126 def requestDownloadFile(self, widget, data=None):
127 _logger.info('Requesting to Download file')
128 if self.treeview.get_selection().count_selected_rows() != 0:
129 model, iterlist = self.treeview.get_selection().get_selected_rows()
130 for path in iterlist:
131 iter = model.get_iter(path)
132 fi = model.get_value(iter, 1)
135 if self.activity._mode == 'SERVER':
136 self.activity._server_download_document( str( model.get_value(iter, 0)) )
139 self.activity._get_document(str( model.get_value(iter, 0)))
141 self._alert(_("All tubes are busy, file download cannot start"),_("Please wait and try again"))
143 self._alert(_("Object has already or is currently being downloaded"))
144 threading.Thread(target=do_down).start()
146 self._alert(_("You must select an object to download"))
149 def _addFileToUIList(self, fileid, fileinfo):
150 modle = self.treeview.get_model()
151 modle.append( None, [fileid, fileinfo])
153 def _remFileFromUIList(self, id):
154 model = self.treeview.get_model()
155 iter = model.get_iter_first()
157 if model.get_value( iter, 0 ) == id:
159 iter = model.iter_next( iter )
164 def show_throbber(self, show, mesg="", addon=None):
167 throbber = gtk.VBox()
169 img.set_from_file('throbber.gif')
170 throbber.pack_start(img)
171 throbber.pack_start(gtk.Label(mesg))
174 throbber.pack_start( addon )
176 self.activity.set_canvas(throbber)
177 self.activity.show_all()
179 self.activity.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
180 self.activity.set_sensitive(False)
183 self.activity.set_canvas(self.activity.disp)
184 self.activity.show_all()
186 self.activity.window.set_cursor(None)
187 self.activity.set_sensitive(True)
189 while gtk.events_pending():
192 def _alert(self, title, text=None, timeout=5):
193 alert = NotifyAlert(timeout=timeout)
194 alert.props.title = title
195 alert.props.msg = text
196 self.activity.add_alert(alert)
197 alert.connect('response', self._alert_cancel_cb)
200 def _alert_cancel_cb(self, alert, response_id):
201 self.activity.remove_alert(alert)
203 def switch_to_server(self, widget, data=None):
204 self.activity.switch_to_server()
206 def showAdmin(self, widget, data=None):
209 userList = self.activity.get_server_user_list()
210 except ServerRequestFailure:
211 self._alert(_("Failed to get user list from server"))
212 self.show_throbber( False )
214 self.show_throbber( False )
215 level = [_("Download Only"), _("Upload/Remove"), _("Admin")]
217 myTable = gtk.Table(10, 1, False)
218 hbbox = gtk.HButtonBox()
219 returnBut = gtk.Button(_("Return to Main Screen"))
220 returnBut.connect("clicked",self.restore_view, None)
227 label = gtk.Label(userList[key][0])
228 label.set_alignment(0, 0)
229 holder.pack_start(label)
231 if key == self.activity._user_key_hash:
232 mode_box = gtk.Label(level[userList[key][1]])
233 mode_box.set_alignment(1,0)
235 mode_box = gtk.combo_box_new_text()
237 mode_box.append_text( option )
239 mode_box.set_active(userList[key][1])
240 mode_box.connect("changed", self.user_changed, key)
242 holder.pack_start(mode_box, False, False, 0)
243 listbox.pack_start(holder, False, False, 0)
245 window = gtk.ScrolledWindow()
246 window.set_policy( gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC )
247 window.add_with_viewport(listbox)
249 myTable.attach(hbbox,0,1,0,1)
250 myTable.attach(window,0,1,1,10)
252 self.lockout_action_menu(True)
253 self.activity.set_canvas(myTable)
254 self.activity.show_all()
256 self.show_throbber(True, _("Requesting user list from server"))
257 threading.Thread(target=call).start()
260 def user_changed(self, widget, id):
261 widget.set_sensitive(False)
264 self.activity.change_server_user(id, widget.get_active())
265 widget.set_sensitive(True)
266 except ServerRequestFailure:
267 parent = widget.get_parent()
268 parent.remove(widget)
269 lbl = gtk.Label(_("User Change Failed"))
270 lbl.set_alignment(1,0)
274 threading.Thread(target=change).start()
276 def restore_view(self, widget, data = None):
277 self.lockout_action_menu(False)
278 self.activity.set_canvas(self.activity.disp)
279 #self.show_throbber( False )
281 def lockout_action_menu(self, set_lock = True):
282 self.guiView.action_bar.set_sensitive(not set_lock)
284 class GuiView(gtk.ScrolledWindow):
286 This class is used to just remove the table setup from the main file
288 def __init__(self, activity):
289 gtk.ScrolledWindow.__init__(self)
290 self.set_policy( gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC )
291 self.activity = activity
292 self.treeview = gtk.TreeView(gtk.TreeStore(str,object))
293 self.guiHandler = GuiHandler( activity, self.treeview, self )
294 #self.build_table(activity)
296 def build_toolbars(self):
297 self.action_buttons = {}
299 # BUILD CUSTOM TOOLBAR
300 self.action_bar = gtk.Toolbar()
301 self.action_buttons['add'] = ToolButton('fs_gtk-add')
302 self.action_buttons['add'].set_tooltip(_("Add Object"))
304 self.action_buttons['rem'] = ToolButton('fs_gtk-remove')
305 self.action_buttons['rem'].set_tooltip(_("Remove Object(s)"))
307 self.action_buttons['save'] = ToolButton('filesave')
308 self.action_buttons['save'].set_tooltip( _("Copy Object(s) to Journal") )
311 self.action_buttons['down'] = ToolButton('epiphany-download')
312 self.action_buttons['down'].set_tooltip( _('Download Object(s)') )
314 self.action_buttons['admin'] = ToolButton('gtk-network')
315 self.action_buttons['admin'].set_tooltip( _('Server Permissions') )
317 self.action_buttons['server'] = ToolButton('gaim-link')
318 self.action_buttons['server'].set_tooltip( _('Connect to Server') )
319 self.action_buttons['server'].set_sensitive( False )
321 if self.activity.isServer:
322 self.action_buttons['add'].connect("clicked", self.guiHandler.requestAddFile, None)
323 self.action_buttons['save'].connect("clicked", self.guiHandler.requestInsFile, None)
324 self.action_buttons['rem'].connect("clicked", self.guiHandler.requestRemFile, None)
325 self.action_buttons['server'].connect("clicked", self.guiHandler.switch_to_server, None)
327 self.action_bar.insert(self.action_buttons['add'], -1)
328 self.action_bar.insert(self.action_buttons['save'], -1)
329 self.action_bar.insert(self.action_buttons['rem'], -1)
330 self.action_bar.insert(self.action_buttons['server'], -1)
332 # Check for server, if found activate connect link
333 def check_server_status():
335 if self.activity.check_for_server():
336 self.action_buttons['server'].set_sensitive( True )
337 except ServerRequestFailure:
339 threading.Thread(target=check_server_status).start()
342 self.action_buttons['down'].connect("clicked", self.guiHandler.requestDownloadFile, None)
343 self.action_bar.insert(self.action_buttons['down'], -1)
345 if self.activity._mode == 'SERVER' and self.activity._user_permissions != 0:
346 self.action_buttons['add'].connect("clicked", self.guiHandler.requestAddFile, {'upload':True})
347 self.action_buttons['rem'].connect("clicked", self.guiHandler.requestRemFile, {'remove':True})
349 self.action_bar.insert(self.action_buttons['add'], -1)
350 self.action_bar.insert(self.action_buttons['rem'], -1)
352 if self.activity._user_permissions == 2:
353 self.action_buttons['admin'].connect("clicked", self.guiHandler.showAdmin, None)
354 self.action_bar.insert(self.action_buttons['admin'], -1)
356 self.action_bar.show_all()
358 self.toolbar_set_selection( False )
361 self.toolbox = ActivityToolbox(self.activity)
363 self.toolbox.add_toolbar(_("Actions"), self.action_bar)
365 self.activity.set_toolbox(self.toolbox)
368 def on_selection_changed(self, selection):
369 if selection.count_selected_rows() == 0:
370 self.toolbar_set_selection(False)
372 self.toolbar_set_selection(True)
374 def toolbar_set_selection(self, selected):
375 require_selection = ['save', 'rem', 'down']
376 for key in require_selection:
378 self.action_buttons[key].set_sensitive( True )
380 self.action_buttons[key].set_sensitive( False )
382 def build_table(self):
386 # Name Cell_data_Func Expand Cell Renderer
388 [ _('Name'), FileInfo.file_name, False, gtk.CellRendererText()],
389 [ _('Description'), FileInfo.file_desc, True, gtk.CellRendererText()],
390 [ _('Tags'), FileInfo.file_tags, False, gtk.CellRendererText()],
391 [ _('Size'), FileInfo.file_size, False, gtk.CellRendererText()],
392 [ '', FileInfo.load_bar, False, gtk.CellRendererProgress()]
395 for col_data in text_cells:
397 colName = gtk.TreeViewColumn(col_data[0], cell)
398 colName.set_cell_data_func(cell, col_data[1])
400 # Should the col expand
401 colName.set_expand(col_data[2])
404 self.treeview.append_column(colName)
406 # make it searchable by name
407 self.treeview.set_search_column(1)
409 # Allow Multiple Selections
410 self.treeview.get_selection().set_mode( gtk.SELECTION_MULTIPLE )
411 self.treeview.get_selection().connect('changed', self.on_selection_changed )
413 # Put table into scroll window to allow it to scroll
414 self.add_with_viewport(self.treeview)
416 def clear_files(self, deleteFile = True):
417 model = self.treeview.get_model()
418 iter = model.get_iter_root()
420 key = model.get_value(iter, 0)
422 # Remove file from UI
423 self.guiHandler._remFileFromUIList(key)
425 # UnRegister File with activity share list
426 self.activity._unregisterShareFile( key )
428 # Attempt to remove file from system
430 self.activity.delete_file( key )
432 iter = model.iter_next(iter)
434 def update_progress(self, id, bytes ):
435 model = self.treeview.get_model()
436 iter = model.get_iter_first()
438 if model.get_value( iter, 0 ) == id:
440 iter = model.iter_next( iter )
443 obj = model.get_value( iter, 1 )
444 obj.update_aquired( bytes )
446 # Store updated versoin of the object
447 self.activity.updateFileObj( id, obj )
448 model.set_value( iter, 1, obj)
450 model.row_changed(model.get_path(iter), iter)
452 def set_installed( self, id, sucessful=True ):
453 model = self.treeview.get_model()
454 iter = model.get_iter_first()
456 if model.get_value( iter, 0 ) == id:
458 iter = model.iter_next( iter )
461 obj = model.get_value( iter, 1 )
467 # Store updated versoin of the object
468 self.activity.updateFileObj( id, obj )
469 model.set_value( iter, 1, obj)
470 model.row_changed(model.get_path(iter), iter)