def __accept_clicked_cb(self, widget): if self._section_view.needs_restart: self._section_toolbar.accept_button.set_sensitive(False) self._section_toolbar.cancel_button.set_sensitive(False) alert = Alert() alert.props.title = _('Warning') alert.props.msg = _('Changes require restart') icon = Icon(icon_name='dialog-cancel') alert.add_button(Gtk.ResponseType.CANCEL, _('Cancel changes'), icon) icon.show() if self._current_option != 'aboutme': icon = Icon(icon_name='dialog-ok') alert.add_button(Gtk.ResponseType.ACCEPT, _('Later'), icon) icon.show() icon = Icon(icon_name='system-restart') alert.add_button(Gtk.ResponseType.APPLY, _('Restart now'), icon) icon.show() self._vbox.pack_start(alert, False, False, 0) self._vbox.reorder_child(alert, 2) alert.connect('response', self.__response_cb) alert.show() else: self._show_main_view()
def _object_chooser(self, mime_type, type_name): chooser = ObjectChooser() matches_mime_type = False response = chooser.run() if response == Gtk.ResponseType.ACCEPT: jobject = chooser.get_selected_object() metadata = jobject.metadata file_path = jobject.file_path if metadata['mime_type'] == mime_type: matches_mime_type = True else: alert = Alert() alert.props.title = _('Invalid object') alert.props.msg = \ _('The selected object must be a %s file' % (type_name)) ok_icon = Icon(icon_name='dialog-ok') alert.add_button(Gtk.ResponseType.OK, _('Ok'), ok_icon) ok_icon.show() alert.connect('response', lambda a, r: self.remove_alert(a)) self.add_alert(alert) alert.show() return matches_mime_type, file_path, metadata['title']
def __accept_clicked_cb(self, widget): if hasattr(self._section_view, "apply"): self._section_view.apply() if self._section_view.needs_restart: self._section_toolbar.accept_button.set_sensitive(False) self._section_toolbar.cancel_button.set_sensitive(False) alert = Alert() alert.props.title = _('Warning') alert.props.msg = _('Changes require restart') if self._section_view.props.is_cancellable: icon = Icon(icon_name='dialog-cancel') alert.add_button(Gtk.ResponseType.CANCEL, _('Cancel changes'), icon) icon.show() if self._current_option not in ('aboutme', 'backup'): icon = Icon(icon_name='dialog-ok') alert.add_button(Gtk.ResponseType.ACCEPT, _('Later'), icon) icon.show() icon = Icon(icon_name='system-restart') alert.add_button(Gtk.ResponseType.APPLY, _('Restart now'), icon) icon.show() self._vbox.pack_start(alert, False, False, 0) self._vbox.reorder_child(alert, 2) alert.connect('response', self.__response_cb) alert.show() else: self._show_main_view()
def __accept_clicked_cb(self, widget): if hasattr(self._section_view, "apply"): self._section_view.apply() if self._section_view.needs_restart: self._section_toolbar.accept_button.set_sensitive(False) self._section_toolbar.cancel_button.set_sensitive(False) alert = Alert() alert.props.title = _("Warning") alert.props.msg = _("Changes require restart") if self._section_view.props.is_cancellable: icon = Icon(icon_name="dialog-cancel") alert.add_button(Gtk.ResponseType.CANCEL, _("Cancel changes"), icon) icon.show() if self._current_option not in ("aboutme", "backup"): icon = Icon(icon_name="dialog-ok") alert.add_button(Gtk.ResponseType.ACCEPT, _("Later"), icon) icon.show() icon = Icon(icon_name="system-restart") alert.add_button(Gtk.ResponseType.APPLY, _("Restart now"), icon) icon.show() self._vbox.pack_start(alert, False, False, 0) self._vbox.reorder_child(alert, 2) alert.connect("response", self.__response_cb) alert.show() else: self._show_main_view()
def _check_current_cell_text(self): """Check the user-entered text for the current cell. If it matches the expected value, also check to see if the user's filled in all blank cells and hence has won. """ # Check whether the answer is correct. If so, change the cell to be # uneditable. expected_num = self._calculate_pascal_number(self._current_cell) if int(self._current_cell_text) == expected_num: self._blank_cells.remove(self._current_cell) self._update_current_cell((-1, -1)) # Check whether all blank cells have been filled. if len(self._blank_cells) == 0: alert = Alert() alert.props.title = _('You\'ve won!') alert.props.msg = _('Well done! You\'ve completed the Pascal ' 'Triangle. Do you want to play again?') icon = Icon(icon_name='emblem-favorite') alert.props.icon = icon icon.show() icon = Icon(icon_name='add') alert.add_button(Gtk.ResponseType.ACCEPT, _('New Game'), icon) icon.show() alert.connect('response', self.__alert_response_cb) alert.show() self._alert = alert self.add_alert(alert)
def can_close(self): if self._force_close: return True elif downloadmanager.can_quit(): return True else: alert = Alert() alert.props.title = ngettext('Download in progress', 'Downloads in progress', downloadmanager.num_downloads()) message = ngettext('Stopping now will erase your download', 'Stopping now will erase your downloads', downloadmanager.num_downloads()) alert.props.msg = message cancel_icon = Icon(icon_name='dialog-cancel') cancel_label = ngettext('Continue download', 'Continue downloads', downloadmanager.num_downloads()) alert.add_button(Gtk.ResponseType.CANCEL, cancel_label, cancel_icon) stop_icon = Icon(icon_name='dialog-ok') alert.add_button(Gtk.ResponseType.OK, _('Stop'), stop_icon) stop_icon.show() self.add_alert(alert) alert.connect('response', self.__inprogress_response_cb) alert.show() self.present() return False
def _show_journal_alert(self, title, msg): _stop_alert = Alert() _stop_alert.props.title = title _stop_alert.props.msg = msg if _HAS_BUNDLE_LAUNCHER: bundle = get_bundle(object_id=self._object_id) if bundle is not None: icon = Icon(file=bundle.get_icon()) label = _('Open with %s') % bundle.get_name() _stop_alert.add_button(Gtk.ResponseType.ACCEPT, label, icon) else: icon = Icon(icon_name='zoom-activity') label = _('Show in Journal') _stop_alert.add_button(Gtk.ResponseType.APPLY, label, icon) icon.show() ok_icon = Icon(icon_name='dialog-ok') _stop_alert.add_button(Gtk.ResponseType.OK, _('Ok'), ok_icon) ok_icon.show() # Remove other alerts for alert in self._alerts: self.remove_alert(alert) self.add_alert(_stop_alert) _stop_alert.connect('response', self.__stop_response_cb) _stop_alert.show()
def _object_chooser(self, mime_type, type_name): chooser = ObjectChooser() matches_mime_type = False response = chooser.run() if response == Gtk.ResponseType.ACCEPT: jobject = chooser.get_selected_object() metadata = jobject.metadata file_path = jobject.file_path if metadata['mime_type'] == mime_type: matches_mime_type = True else: alert = Alert() alert.props.title = _('Invalid object') alert.props.msg = \ _('The selected object must be a %s file' % (type_name)) ok_icon = Icon(icon_name='dialog-ok') alert.add_button(Gtk.ResponseType.OK, _('Ok'), ok_icon) ok_icon.show() alert.connect('response', lambda a, r: self.remove_alert(a)) self.add_alert(alert) alert.show() return matches_mime_type, file_path, metadata['title']
def _value_changed(self, cell, path, new_text, model, activity): _logger.info("Change '%s' to '%s'" % (model[path][1], new_text)) is_number = True number = new_text.replace(",", ".") try: float(number) except ValueError: is_number = False if is_number: decimals = utils.get_decimals(str(float(number))) new_text = locale.format('%.' + decimals + 'f', float(number)) model[path][1] = str(new_text) self.emit("value-changed", str(path), number) elif not is_number: alert = Alert() alert.props.title = _('Invalid Value') alert.props.msg = \ _('The value must be a number (integer or decimal)') ok_icon = Icon(icon_name='dialog-ok') alert.add_button(Gtk.ResponseType.OK, _('Ok'), ok_icon) ok_icon.show() alert.connect('response', lambda a, r: activity.remove_alert(a)) activity.add_alert(alert) alert.show()
def __accept_clicked_cb(self, widget): if hasattr(self._section_view, "apply"): self._section_view.apply() if self._section_view.needs_restart: self._section_toolbar.accept_button.set_sensitive(False) self._section_toolbar.cancel_button.set_sensitive(False) alert = Alert() alert.props.title = _('Warning') alert.props.msg = _('Changes require restart') if self._section_view.props.is_cancellable: icon = Icon(icon_name='dialog-cancel') alert.add_button(Gtk.ResponseType.CANCEL, _('Cancel changes'), icon) icon.show() if self._section_view.props.is_deferrable: icon = Icon(icon_name='dialog-ok') alert.add_button(Gtk.ResponseType.ACCEPT, _('Later'), icon) icon.show() icon = Icon(icon_name='system-restart') alert.add_button(Gtk.ResponseType.APPLY, _('Restart now'), icon) icon.show() self.add_alert(alert) alert.connect('response', self.__response_cb) alert.show() else: self._show_main_view()
def _show_journal_alert(self, title, msg): _stop_alert = Alert() _stop_alert.props.title = title _stop_alert.props.msg = msg if _HAS_BUNDLE_LAUNCHER: bundle = get_bundle(object_id=self._object_id) if bundle is not None: icon = Icon(file=bundle.get_icon()) label = _('Open with %s') % bundle.get_name() _stop_alert.add_button(Gtk.ResponseType.ACCEPT, label, icon) else: icon = Icon(icon_name='zoom-activity') label = _('Show in Journal') _stop_alert.add_button(Gtk.ResponseType.APPLY, label, icon) icon.show() ok_icon = Icon(icon_name='dialog-ok') _stop_alert.add_button(Gtk.ResponseType.OK, _('Ok'), ok_icon) ok_icon.show() # Remove other alerts for alert in self._alerts: self.remove_alert(alert) self.add_alert(_stop_alert) _stop_alert.connect('response', self.__stop_response_cb) _stop_alert.show()
def _value_changed(self, cell, path, new_text, model, activity): _logger.debug("Change '%s' to '%s'" % (model[path][1], new_text)) is_number = True number = new_text.replace(",", ".") try: float(number) except ValueError: is_number = False if is_number: decimals = utils.get_decimals(str(float(number))) new_text = locale.format('%.' + decimals + 'f', float(number)) model[path][1] = str(new_text) self.emit("value-changed", str(path), number) elif not is_number: alert = Alert() alert.props.title = _('Invalid Value') alert.props.msg = \ _('The value must be a number (integer or decimal)') ok_icon = Icon(icon_name='dialog-ok') alert.add_button(Gtk.ResponseType.OK, _('Ok'), ok_icon) ok_icon.show() alert.connect('response', lambda a, r: activity.remove_alert(a)) activity.add_alert(alert) alert.show()
def _new_game_alert(self): alert = Alert() alert.props.title = _('New game') alert.props.msg = _('Do you want to play a new game?') icon = Icon(icon_name='dialog-cancel') alert.add_button(Gtk.ResponseType.CANCEL, _('Cancel'), icon) icon.show() ok_icon = Icon(icon_name='dialog-ok') alert.add_button(Gtk.ResponseType.OK, _('New game'), ok_icon) ok_icon.show() alert.connect('response', self.__game_alert_response_cb) self._parent.add_alert(alert) alert.show()
def _invalid_number_alert(activity): alert = Alert() alert.props.title = _('Invalid Value') alert.props.msg = _('The value must be a number (integer or decimal)') ok_icon = Icon(icon_name='dialog-ok') alert.add_button(Gtk.ResponseType.OK, _('Ok'), ok_icon) ok_icon.show() alert.connect('response', lambda a, r: activity.remove_alert(a)) activity.add_alert(alert) alert.show()
def _search_clicked_cb(self, widget): title = self.searchentry.get_text() wiki = self.wikimenu.combo.props.value if not title: return if book.wiki.find('%s (from %s)' % (title, wiki))[0]: alert = Alert() alert.props.title = _('Exists') alert.props.msg = _('"%s" article already exists' % title) alert.show() else: Timer(0, self._download, [title, wiki]).start()
def __erase_activate_cb(self, menu_item): alert = Alert() erase_string = _("Erase") alert.props.title = erase_string alert.props.msg = _('Do you want to permanently erase "%s"?') % self._metadata["title"] icon = Icon(icon_name="dialog-cancel") alert.add_button(Gtk.ResponseType.CANCEL, _("Cancel"), icon) icon.show() ok_icon = Icon(icon_name="dialog-ok") alert.add_button(Gtk.ResponseType.OK, erase_string, ok_icon) ok_icon.show() alert.connect("response", self.__erase_alert_response_cb) journalwindow.get_journal_window().add_alert(alert) alert.show()
def _erase_button_clicked_cb(self, button): alert = Alert() erase_string = _('Erase') alert.props.title = erase_string alert.props.msg = _('Do you want to permanently erase \"%s\"?') \ % self._metadata['title'] icon = Icon(icon_name='dialog-cancel') alert.add_button(Gtk.ResponseType.CANCEL, _('Cancel'), icon) icon.show() ok_icon = Icon(icon_name='dialog-ok') alert.add_button(Gtk.ResponseType.OK, erase_string, ok_icon) ok_icon.show() alert.connect('response', self.__erase_alert_response_cb) journalwindow.get_journal_window().add_alert(alert) alert.show()
def __erase_activate_cb(self, menu_item): alert = Alert() erase_string = _('Erase') alert.props.title = erase_string alert.props.msg = _('Do you want to permanently erase \"%s\"?') \ % self._metadata['title'] icon = Icon(icon_name='dialog-cancel') alert.add_button(Gtk.ResponseType.CANCEL, _('Cancel'), icon) icon.show() ok_icon = Icon(icon_name='dialog-ok') alert.add_button(Gtk.ResponseType.OK, erase_string, ok_icon) ok_icon.show() alert.connect('response', self.__erase_alert_response_cb) journalwindow.get_journal_window().add_alert(alert) alert.show()
def _save_epub(self): epub_file_name = create_ebub_from_book_model( self.metadata['title'], self._book_model) # create a new journal item fileObject = datastore.create() fileObject.metadata['title'] = \ _('"%s" as book') % self.metadata['title'] fileObject.metadata['mime_type'] = 'application/epub+zip' full_text = '' for page in self._book_model.get_pages(): full_text += page.text + '\n' fileObject.metadata['fulltext'] = full_text fileObject.metadata['icon-color'] = self.metadata['icon-color'] fileObject.metadata['keep'] = self.metadata.get('keep', '0') fileObject.metadata['preview'] = self.metadata['preview'] fileObject.file_path = epub_file_name # store the journal item datastore.write(fileObject, transfer_ownership=True) book_object_id = fileObject.object_id fileObject.destroy() del fileObject shutil.rmtree(os.path.dirname(epub_file_name)) finish_alert = Alert() finish_alert.props.title = _('Book created') finish_alert.props.msg = _('You can read the book in your Journal') open_icon = Icon(icon_name='zoom-activity') finish_alert.add_button(Gtk.ResponseType.APPLY, _('Show in Journal'), open_icon) open_icon.show() ok_icon = Icon(icon_name='dialog-ok') finish_alert.add_button(Gtk.ResponseType.OK, _('Ok'), ok_icon) ok_icon.show() # Remove other alerts for alert in self._alerts: self.remove_alert(alert) self.add_alert(finish_alert) finish_alert.connect('response', self.__book_saved_alert_response_cb, book_object_id) finish_alert.show()
def _save_epub(self): epub_file_name = create_ebub_from_book_model(self.metadata['title'], self._book_model) # create a new journal item fileObject = datastore.create() fileObject.metadata['title'] = \ _('"%s" as book') % self.metadata['title'] fileObject.metadata['mime_type'] = 'application/epub+zip' full_text = '' for page in self._book_model.get_pages(): full_text += page.text + '\n' fileObject.metadata['fulltext'] = full_text fileObject.metadata['icon-color'] = self.metadata['icon-color'] fileObject.metadata['keep'] = self.metadata.get('keep', '0') fileObject.metadata['preview'] = self.metadata['preview'] fileObject.file_path = epub_file_name # store the journal item datastore.write(fileObject, transfer_ownership=True) book_object_id = fileObject.object_id fileObject.destroy() del fileObject shutil.rmtree(os.path.dirname(epub_file_name)) finish_alert = Alert() finish_alert.props.title = _('Book created') finish_alert.props.msg = _('You can read the book in your Journal') open_icon = Icon(icon_name='zoom-activity') finish_alert.add_button(Gtk.ResponseType.APPLY, _('Show in Journal'), open_icon) open_icon.show() ok_icon = Icon(icon_name='dialog-ok') finish_alert.add_button(Gtk.ResponseType.OK, _('Ok'), ok_icon) ok_icon.show() # Remove other alerts for alert in self._alerts: self.remove_alert(alert) self.add_alert(finish_alert) finish_alert.connect('response', self.__book_saved_alert_response_cb, book_object_id) finish_alert.show()
def _show_journal_alert(self, title, msg, object_id): open_alert = Alert() open_alert.props.title = title open_alert.props.msg = msg open_icon = Icon(icon_name='zoom-activity') open_alert.add_button(Gtk.ResponseType.APPLY, _('Show in Journal'), open_icon) open_icon.show() ok_icon = Icon(icon_name='dialog-ok') open_alert.add_button(Gtk.ResponseType.OK, _('Ok'), ok_icon) ok_icon.show() # Remove other alerts for alert in self._alerts: self.remove_alert(alert) self.add_alert(open_alert) open_alert.connect('response', self.__open_response_cb, object_id) open_alert.show()
def _show_journal_alert(self, title, msg, object_id): open_alert = Alert() open_alert.props.title = title open_alert.props.msg = msg open_icon = Icon(icon_name='zoom-activity') open_alert.add_button(Gtk.ResponseType.APPLY, _('Show in Journal'), open_icon) open_icon.show() ok_icon = Icon(icon_name='dialog-ok') open_alert.add_button(Gtk.ResponseType.OK, _('Ok'), ok_icon) ok_icon.show() # Remove other alerts for alert in self._alerts: self.remove_alert(alert) self.add_alert(open_alert) open_alert.connect('response', self.__open_response_cb, object_id) open_alert.show()
def _restart_alert(self): alert = Alert() alert.props.title = _('Warning') alert.props.msg = _('Changes require restart') icon = Icon(icon_name='dialog-cancel') alert.add_button(Gtk.ResponseType.CANCEL, _('Cancel changes'), icon) icon.show() icon = Icon(icon_name='dialog-ok') alert.add_button(Gtk.ResponseType.ACCEPT, _('Later'), icon) icon.show() icon = Icon(icon_name='system-restart') alert.add_button(Gtk.ResponseType.APPLY, _('Restart now'), icon) icon.show() alert.connect('response', self.__response_cb) self.add_alert(alert) alert.show()
def _erase_comment_cb(self, widget, event): alert = Alert() entry = self.get_selection().get_selected()[1] erase_string = _('Erase') alert.props.title = erase_string alert.props.msg = _('Do you want to permanently erase \"%s\"?') \ % self._store[entry][self.COMMENT_MESSAGE] icon = Icon(icon_name='dialog-cancel') alert.add_button(Gtk.ResponseType.CANCEL, _('Cancel'), icon) icon.show() ok_icon = Icon(icon_name='dialog-ok') alert.add_button(Gtk.ResponseType.OK, erase_string, ok_icon) ok_icon.show() alert.connect('response', self._erase_alert_response_cb, entry) journalwindow.get_journal_window().add_alert(alert) alert.show()
def _erase_comment_cb(self, widget, event): alert = Alert() entry = self.get_selection().get_selected()[1] erase_string = _('Erase') alert.props.title = erase_string alert.props.msg = _('Do you want to permanently erase \"%s\"?') \ % self._store[entry][self.COMMENT_MESSAGE] icon = Icon(icon_name='dialog-cancel') alert.add_button(Gtk.ResponseType.CANCEL, _('Cancel'), icon) icon.show() ok_icon = Icon(icon_name='dialog-ok') alert.add_button(Gtk.ResponseType.OK, erase_string, ok_icon) ok_icon.show() alert.connect('response', self._erase_alert_response_cb, entry) journalwindow.get_journal_window().add_alert(alert) alert.show()
def __create_restart_alert_cb(self, widget=None, event=None): alert = Alert() alert.props.title = _("Warning") alert.props.msg = self._section_view.restart_msg if self._section_view.props.is_cancellable: icon = Icon(icon_name="dialog-cancel") alert.add_button(Gtk.ResponseType.CANCEL, _("Cancel changes"), icon) icon.show() if self._section_view.props.is_deferrable: icon = Icon(icon_name="dialog-ok") alert.add_button(Gtk.ResponseType.ACCEPT, _("Later"), icon) icon.show() icon = Icon(icon_name="system-restart") alert.add_button(Gtk.ResponseType.APPLY, _("Restart now"), icon) icon.show() self.add_alert(alert) alert.connect("response", self.__response_cb) alert.show()
def __create_restart_alert_cb(self, widget=None, event=None): alert = Alert() alert.props.title = _('Warning') alert.props.msg = self._section_view.restart_msg if self._section_view.props.is_cancellable: icon = Icon(icon_name='dialog-cancel') alert.add_button(Gtk.ResponseType.CANCEL, _('Cancel changes'), icon) icon.show() if self._section_view.props.is_deferrable: icon = Icon(icon_name='dialog-ok') alert.add_button(Gtk.ResponseType.ACCEPT, _('Later'), icon) icon.show() icon = Icon(icon_name='system-restart') alert.add_button(Gtk.ResponseType.APPLY, _('Restart now'), icon) icon.show() self.add_alert(alert) alert.connect('response', self.__response_cb) alert.show()
def __create_restart_alert_cb(self, widget=None, event=None): alert = Alert() alert.props.title = _('Warning') alert.props.msg = self._section_view.restart_msg if self._section_view.props.is_cancellable: icon = Icon(icon_name='dialog-cancel') alert.add_button(Gtk.ResponseType.CANCEL, _('Cancel changes'), icon) icon.show() if self._section_view.props.is_deferrable: icon = Icon(icon_name='dialog-ok') alert.add_button(Gtk.ResponseType.ACCEPT, _('Later'), icon) icon.show() icon = Icon(icon_name='system-restart') alert.add_button(Gtk.ResponseType.APPLY, _('Restart now'), icon) icon.show() self.add_alert(alert) alert.connect('response', self.__response_cb) alert.show()
class Download(object): def __init__(self, download, browser): self._download = download self._activity = browser.get_toplevel() self._source = download.get_uri() self._download.connect('notify::status', self.__state_change_cb) self._download.connect('error', self.__error_cb) self.datastore_deleted_handler = None self.dl_jobject = None self._object_id = None self._stop_alert = None self._progress = 0 self._last_update_progress = 0 self._progress_sid = None # figure out download URI self.temp_path = os.path.join(activity.get_activity_root(), 'instance') if not os.path.exists(self.temp_path): os.makedirs(self.temp_path) fd, self._dest_path = tempfile.mkstemp( dir=self.temp_path, suffix=download.get_suggested_filename(), prefix='tmp') os.close(fd) logging.debug('Download destination path: %s' % self._dest_path) # We have to start the download to get 'total-size' # property. It not, 0 is returned self._download.set_destination_uri('file://' + self._dest_path) self._download.start() def _update_progress(self): if self._progress > self._last_update_progress: self._last_update_progress = self._progress self.dl_jobject.metadata['progress'] = str(self._progress) datastore.write(self.dl_jobject) self._progress_sid = None return False def __progress_change_cb(self, download, something): self._progress = int(self._download.get_progress() * 100) if self._progress_sid is None: self._progress_sid = GObject.timeout_add(PROGRESS_TIMEOUT, self._update_progress) def __current_size_changed_cb(self, download, something): current_size = self._download.get_current_size() total_size = self._download.get_total_size() self._progress = int(current_size * 100 / total_size) if self._progress_sid is None: self._progress_sid = GObject.timeout_add(PROGRESS_TIMEOUT, self._update_progress) def __state_change_cb(self, download, gparamspec): state = self._download.get_status() if state == WebKit.DownloadStatus.STARTED: # Check free space and cancel the download if there is not enough. total_size = self._download.get_total_size() logging.debug('Total size of the file: %s', total_size) enough_space = self.enough_space(total_size, path=self.temp_path) if not enough_space: logging.debug('Download canceled because of Disk Space') self.cancel() self._canceled_alert = Alert() self._canceled_alert.props.title = _('Not enough space ' 'to download') total_size_mb = total_size / 1024.0**2 free_space_mb = (self._free_available_space( path=self.temp_path) - SPACE_THRESHOLD) \ / 1024.0 ** 2 filename = self._download.get_suggested_filename() self._canceled_alert.props.msg = \ _('Download "%{filename}" requires %{total_size_in_mb}' ' MB of free space, only %{free_space_in_mb} MB' ' is available' % {'filename': filename, 'total_size_in_mb': format_float(total_size_mb), 'free_space_in_mb': format_float(free_space_mb)}) ok_icon = Icon(icon_name='dialog-ok') self._canceled_alert.add_button(Gtk.ResponseType.OK, _('Ok'), ok_icon) ok_icon.show() self._canceled_alert.connect('response', self.__stop_response_cb) self._activity.add_alert(self._canceled_alert) else: # FIXME: workaround for SL #4385 # self._download.connect('notify::progress', # self.__progress_change_cb) self._download.connect('notify::current-size', self.__current_size_changed_cb) self._create_journal_object() self._object_id = self.dl_jobject.object_id alert = TimeoutAlert(9) alert.props.title = _('Download started') alert.props.msg = _('%s' % self._download.get_suggested_filename()) self._activity.add_alert(alert) alert.connect('response', self.__start_response_cb) alert.show() global _active_downloads _active_downloads.append(self) elif state == WebKit.DownloadStatus.FINISHED: self._stop_alert = Alert() self._stop_alert.props.title = _('Download completed') self._stop_alert.props.msg = \ _('%s' % self._download.get_suggested_filename()) if self._progress_sid is not None: GObject.source_remove(self._progress_sid) self.dl_jobject.metadata['title'] = \ self._download.get_suggested_filename() self.dl_jobject.metadata['description'] = _('From: %s') \ % self._source self.dl_jobject.metadata['progress'] = '100' self.dl_jobject.file_path = self._dest_path # sniff for a mime type, no way to get headers from WebKit sniffed_mime_type = mime.get_for_file(self._dest_path) self.dl_jobject.metadata['mime_type'] = sniffed_mime_type if sniffed_mime_type in ('image/bmp', 'image/gif', 'image/jpeg', 'image/png', 'image/tiff'): preview = self._get_preview() if preview is not None: self.dl_jobject.metadata['preview'] = \ dbus.ByteArray(preview) datastore.write(self.dl_jobject, transfer_ownership=True, reply_handler=self.__internal_save_cb, error_handler=self.__internal_error_cb, timeout=360) bundle = None if _HAS_BUNDLE_LAUNCHER: bundle = get_bundle(object_id=self._object_id) if bundle is not None: icon = Icon(file=bundle.get_icon()) label = _('Open with %s') % bundle.get_name() response_type = Gtk.ResponseType.APPLY else: icon = Icon(icon_name='zoom-activity') label = _('Show in Journal') response_type = Gtk.ResponseType.ACCEPT self._stop_alert.add_button(response_type, label, icon) icon.show() ok_icon = Icon(icon_name='dialog-ok') self._stop_alert.add_button(Gtk.ResponseType.OK, _('Ok'), ok_icon) ok_icon.show() self._activity.add_alert(self._stop_alert) self._stop_alert.connect('response', self.__stop_response_cb) self._stop_alert.show() elif state == WebKit.DownloadStatus.CANCELLED: self.cleanup() def __error_cb(self, download, err_code, err_detail, reason): logging.debug('Error downloading URI code %s, detail %s: %s' % (err_code, err_detail, reason)) def __internal_save_cb(self): logging.debug('Object saved succesfully to the datastore.') self.cleanup() def __internal_error_cb(self, err): logging.debug('Error saving activity object to datastore: %s' % err) self.cleanup() def __start_response_cb(self, alert, response_id): global _active_downloads if response_id is Gtk.ResponseType.CANCEL: logging.debug('Download Canceled') self.cancel() try: datastore.delete(self._object_id) except Exception, e: logging.warning('Object has been deleted already %s' % e) self.cleanup() if self._stop_alert is not None: self._activity.remove_alert(self._stop_alert) self._activity.remove_alert(alert)
class JukeboxActivity(activity.Activity): __gsignals__ = { 'playlist-finished': (GObject.SignalFlags.RUN_FIRST, None, []), } def __init__(self, handle): activity.Activity.__init__(self, handle) self.player = None self._alert = None self._playlist_jobject = None self.set_title(_('Jukebox Activity')) self.max_participants = 1 toolbar_box = ToolbarBox() activity_button = ActivityToolbarButton(self) activity_toolbar = activity_button.page toolbar_box.toolbar.insert(activity_button, 0) self.title_entry = activity_toolbar.title self._view_toolbar = ViewToolbar() self._view_toolbar.connect('go-fullscreen', self.__go_fullscreen_cb) self._view_toolbar.connect('toggle-playlist', self.__toggle_playlist_cb) view_toolbar_button = ToolbarButton(page=self._view_toolbar, icon_name='toolbar-view') self._view_toolbar.show() toolbar_box.toolbar.insert(view_toolbar_button, -1) view_toolbar_button.show() self._control_toolbar = Gtk.Toolbar() self._control_toolbar_button = ToolbarButton( page=self._control_toolbar, icon_name='media-playback-start') self._control_toolbar.show() toolbar_box.toolbar.insert(self._control_toolbar_button, -1) self._control_toolbar_button.hide() self.set_toolbar_box(toolbar_box) toolbar_box.show_all() self.connect('key_press_event', self.__key_press_event_cb) self.connect('playlist-finished', self.__playlist_finished_cb) # We want to be notified when the activity gets the focus or # loses it. When it is not active, we don't need to keep # reproducing the video self.connect('notify::active', self.__notify_active_cb) self._video_canvas = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) self._playlist_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.playlist_widget = PlayList() self.playlist_widget.connect('play-index', self.__play_index_cb) self.playlist_widget.connect('missing-tracks', self.__missing_tracks_cb) self.playlist_widget.set_size_request( Gdk.Screen.width() * PLAYLIST_WIDTH_PROP, 0) self.playlist_widget.show() self._playlist_box.pack_start(self.playlist_widget, expand=True, fill=True, padding=0) self._playlist_toolbar = Gtk.Toolbar() move_up = ToolButton("go-up") move_up.set_tooltip(_("Move up")) move_up.connect("clicked", self._move_up_cb) self._playlist_toolbar.insert(move_up, 0) move_down = ToolButton("go-down") move_down.set_tooltip(_("Move down")) move_down.connect("clicked", self._move_down_cb) self._playlist_toolbar.insert(move_down, 1) self._playlist_box.pack_end(self._playlist_toolbar, False, False, 0) self._video_canvas.pack_start(self._playlist_box, False, False, 0) # Create the player just once logging.debug('Instantiating GstPlayer') self.player = GstPlayer() self.player.connect('eos', self.__player_eos_cb) self.player.connect('error', self.__player_error_cb) self.player.connect('play', self.__player_play_cb) self.control = Controls(self, toolbar_box.toolbar, self._control_toolbar) self._separator = Gtk.SeparatorToolItem() self._separator.props.draw = False self._separator.set_expand(True) self._separator.show() toolbar_box.toolbar.insert(self._separator, -1) self._stop = StopButton(self) toolbar_box.toolbar.insert(self._stop, -1) self._empty_widget = Gtk.Label(label="") self._empty_widget.show() self.videowidget = VideoWidget() self.set_canvas(self._video_canvas) self._init_view_area() self.show_all() # need hide the playlist by default self._playlist_box.hide() self._configure_cb() self.player.init_view_area(self.videowidget) self._volume_monitor = Gio.VolumeMonitor.get() self._volume_monitor.connect('mount-added', self.__mount_added_cb) self._volume_monitor.connect('mount-removed', self.__mount_removed_cb) if handle.object_id is None: # The activity was launched from scratch. We need to show # the Empty Widget self.playlist_widget.hide() emptypanel.show(self, 'activity-jukebox', _('No media'), _('Choose media files'), self.control.show_picker_cb) self.control.check_if_next_prev() Gdk.Screen.get_default().connect('size-changed', self._configure_cb) def _move_up_cb(self, button): self.playlist_widget.move_up() def _move_down_cb(self, button): self.playlist_widget.move_down() def _configure_cb(self, event=None): toolbar = self.get_toolbar_box().toolbar toolbar.remove(self._stop) toolbar.remove(self._separator) if Gdk.Screen.width() < Gdk.Screen.height(): self._control_toolbar_button.show() self._control_toolbar_button.set_expanded(True) self.control.update_layout(landscape=False) toolbar.insert(self._separator, -1) else: self._control_toolbar_button.set_expanded(False) self._control_toolbar_button.hide() self.control.update_layout(landscape=True) toolbar.insert(self._stop, -1) def __notify_active_cb(self, widget, event): """Sugar notify us that the activity is becoming active or inactive. When we are inactive, we stop the player if it is reproducing a video. """ logging.debug('JukeboxActivity notify::active signal received') if self.player.player.props.current_uri is not None and \ self.player.playing_video(): if not self.player.is_playing() and self.props.active: self.player.play() if self.player.is_playing() and not self.props.active: self.player.pause() def _init_view_area(self): """ Use a notebook with two pages, one empty an another with the videowidget """ self.view_area = Gtk.Notebook() self.view_area.set_show_tabs(False) self.view_area.append_page(self._empty_widget, None) self.view_area.append_page(self.videowidget, None) self._video_canvas.pack_end(self.view_area, expand=True, fill=True, padding=0) def _switch_canvas(self, show_video): """Show or hide the video visualization in the canvas. When hidden, the canvas is filled with an empty widget to ensure redrawing. """ if show_video: self.view_area.set_current_page(1) else: self.view_area.set_current_page(0) self._video_canvas.queue_draw() def __key_press_event_cb(self, widget, event): keyname = Gdk.keyval_name(event.keyval) logging.info("Keyname Press: %s, time: %s", keyname, event.time) if self.title_entry.has_focus(): return False if keyname == "space": self.control._button_clicked_cb(None) return True def __playlist_finished_cb(self, widget): self._switch_canvas(show_video=False) self._view_toolbar._show_playlist.set_active(True) self.unfullscreen() # Select the first stream to be played when Play button will # be pressed self.playlist_widget.set_current_playing(0) self.control.check_if_next_prev() def songchange(self, direction): current_playing = self.playlist_widget.get_current_playing() if direction == 'prev' and current_playing > 0: self.play_index(current_playing - 1) elif direction == 'next' and \ current_playing < len(self.playlist_widget._items) - 1: self.play_index(current_playing + 1) else: self.emit('playlist-finished') def play_index(self, index): # README: this line is no more necessary because of the # .playing_video() method # self._switch_canvas(show_video=True) self.playlist_widget.set_current_playing(index) path = self.playlist_widget._items[index]['path'] if self.playlist_widget.check_available_media(path): if self.playlist_widget.is_from_journal(path): path = self.playlist_widget.get_path_from_journal(path) self.control.check_if_next_prev() self.player.set_uri(path) self.player.play() else: self.songchange('next') def __play_index_cb(self, widget, index, path): # README: this line is no more necessary because of the # .playing_video() method # self._switch_canvas(show_video=True) self.playlist_widget.set_current_playing(index) if self.playlist_widget.is_from_journal(path): path = self.playlist_widget.get_path_from_journal(path) self.control.check_if_next_prev() self.player.set_uri(path) self.player.play() def __player_eos_cb(self, widget): self.songchange('next') def _show_error_alert(self, title, msg=None): self._alert = ErrorAlert() self._alert.props.title = title if msg is not None: self._alert.props.msg = msg self.add_alert(self._alert) self._alert.connect('response', self._alert_cancel_cb) self._alert.show() def __mount_added_cb(self, volume_monitor, device): logging.debug('Mountpoint added. Checking...') self.remove_alert(self._alert) self.playlist_widget.update() def __mount_removed_cb(self, volume_monitor, device): logging.debug('Mountpoint removed. Checking...') self.remove_alert(self._alert) self.playlist_widget.update() def __missing_tracks_cb(self, widget, tracks): self._show_missing_tracks_alert(tracks) def _show_missing_tracks_alert(self, tracks): self._alert = Alert() title = _('%s tracks not found.') % len(tracks) self._alert.props.title = title icon = Icon(icon_name='dialog-cancel') self._alert.add_button(Gtk.ResponseType.CANCEL, _('Dismiss'), icon) icon.show() icon = Icon(icon_name='dialog-ok') self._alert.add_button(Gtk.ResponseType.APPLY, _('Details'), icon) icon.show() self.add_alert(self._alert) self._alert.connect('response', self.__missing_tracks_alert_response_cb, tracks) def __missing_tracks_alert_response_cb(self, alert, response_id, tracks): if response_id == Gtk.ResponseType.APPLY: vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) vbox.props.valign = Gtk.Align.CENTER label = Gtk.Label(label='') label.set_markup(_('<b>Missing tracks</b>')) vbox.pack_start(label, False, False, 15) for track in tracks: label = Gtk.Label(label=track['path']) vbox.add(label) _missing_tracks = Gtk.ScrolledWindow() _missing_tracks.add_with_viewport(vbox) _missing_tracks.show_all() self.view_area.append_page(_missing_tracks, None) self.view_area.set_current_page(2) self.remove_alert(alert) def _alert_cancel_cb(self, alert, response_id): self.remove_alert(alert) def __player_play_cb(self, widget): # Do not show the visualization widget if we are playing just # an audio stream def callback(): if self.player.playing_video(): self._switch_canvas(True) else: self._switch_canvas(False) return False # HACK: we need a timeout here because gstreamer returns # n-video = 0 if we call it immediately GObject.timeout_add(1000, callback) def __player_error_cb(self, widget, message, detail): self.player.stop() self.control.set_disabled() logging.error('ERROR MESSAGE: %s', message) logging.error('ERROR DETAIL: %s', detail) file_path = self.playlist_widget._items[ self.playlist_widget.get_current_playing()]['path'] mimetype = mime.get_for_file(file_path) title = _('Error') msg = _('This "%s" file can\'t be played') % mimetype self._switch_canvas(False) self._show_error_alert(title, msg) def can_close(self): # We need to put the Gst.State in NULL so gstreamer can # cleanup the pipeline self.player.stop() return True def read_file(self, file_path): """Load a file from the datastore on activity start.""" logging.debug('JukeBoxAtivity.read_file: %s', file_path) title = self.metadata['title'] self.playlist_widget.load_file(file_path, title) self._view_toolbar._show_playlist.set_active(True) def write_file(self, file_path): def write_playlist_to_file(file_path): """Open the file at file_path and write the playlist. It is saved in audio/x-mpegurl format. """ list_file = open(file_path, 'w') for uri in self.playlist_widget._items: list_file.write('#EXTINF:%s\n' % uri['title']) list_file.write('%s\n' % uri['path']) list_file.close() if not self.metadata['mime_type']: self.metadata['mime_type'] = 'audio/x-mpegurl' if self.metadata['mime_type'] == 'audio/x-mpegurl': write_playlist_to_file(file_path) else: if self._playlist_jobject is None: self._playlist_jobject = \ self.playlist_widget.create_playlist_jobject() # Add the playlist to the playlist jobject description. # This is only done if the activity was not started from a # playlist or from scratch: description = '' for uri in self.playlist_widget._items: description += '%s\n' % uri['title'] self._playlist_jobject.metadata['description'] = description write_playlist_to_file(self._playlist_jobject.file_path) datastore.write(self._playlist_jobject) def __go_fullscreen_cb(self, toolbar): self.fullscreen() def __toggle_playlist_cb(self, toolbar): if self._view_toolbar._show_playlist.get_active(): self._playlist_box.show_all() else: self._playlist_box.hide() self._video_canvas.queue_draw()
class Network(SectionView): def __init__(self, model, alerts): SectionView.__init__(self) self._model = model self.restart_alerts = alerts self._jabber_sid = 0 self._radio_valid = True self._jabber_change_handler = None self._radio_change_handler = None self._wireless_configuration_reset_handler = None self._start_jabber = self._model.get_jabber() self._proxy_settings = {} self._proxy_inline_alerts = {} self.set_border_width(style.DEFAULT_SPACING * 2) self.set_spacing(style.DEFAULT_SPACING) group = Gtk.SizeGroup(Gtk.SizeGroupMode.HORIZONTAL) self._radio_alert_box = Gtk.HBox(spacing=style.DEFAULT_SPACING) scrolled = Gtk.ScrolledWindow() scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) self.add(scrolled) scrolled.show() workspace = Gtk.VBox() scrolled.add_with_viewport(workspace) workspace.show() separator_wireless = Gtk.HSeparator() workspace.pack_start(separator_wireless, False, True, 0) separator_wireless.show() label_wireless = Gtk.Label(label=_('Wireless')) label_wireless.set_alignment(0, 0) workspace.pack_start(label_wireless, False, True, 0) label_wireless.show() box_wireless = Gtk.VBox() box_wireless.set_border_width(style.DEFAULT_SPACING * 2) box_wireless.set_spacing(style.DEFAULT_SPACING) radio_info = Gtk.Label(label=_('The wireless radio may be turned' ' off to save battery life.')) radio_info.set_alignment(0, 0) radio_info.set_line_wrap(True) radio_info.show() box_wireless.pack_start(radio_info, False, True, 0) box_radio = Gtk.HBox(spacing=style.DEFAULT_SPACING) self._button = Gtk.CheckButton() self._button.set_alignment(0, 0) box_radio.pack_start(self._button, False, True, 0) self._button.show() label_radio = Gtk.Label(label=_('Radio')) label_radio.set_alignment(0, 0.5) box_radio.pack_start(label_radio, False, True, 0) label_radio.show() box_wireless.pack_start(box_radio, False, True, 0) box_radio.show() self._radio_alert = InlineAlert() self._radio_alert_box.pack_start(self._radio_alert, False, True, 0) box_radio.pack_end(self._radio_alert_box, False, True, 0) self._radio_alert_box.show() if 'radio' in self.restart_alerts: self._radio_alert.props.msg = self.restart_msg self._radio_alert.show() wireless_info = Gtk.Label( label=_('Discard wireless connections if' ' you have trouble connecting to the network')) wireless_info.set_alignment(0, 0) wireless_info.set_line_wrap(True) wireless_info.show() box_wireless.pack_start(wireless_info, False, True, 0) box_clear_wireless = Gtk.HBox(spacing=style.DEFAULT_SPACING) self._clear_wireless_button = Gtk.Button() self._clear_wireless_button.set_label( _('Discard wireless connections')) box_clear_wireless.pack_start(self._clear_wireless_button, False, True, 0) if not self._model.have_wireless_networks(): self._clear_wireless_button.set_sensitive(False) self._clear_wireless_button.show() box_wireless.pack_start(box_clear_wireless, False, True, 0) box_clear_wireless.show() workspace.pack_start(box_wireless, False, True, 0) box_wireless.show() separator_mesh = Gtk.HSeparator() workspace.pack_start(separator_mesh, False, False, 0) separator_mesh.show() label_mesh = Gtk.Label(label=_('Collaboration')) label_mesh.set_alignment(0, 0) workspace.pack_start(label_mesh, False, True, 0) label_mesh.show() box_mesh = Gtk.VBox() box_mesh.set_border_width(style.DEFAULT_SPACING * 2) box_mesh.set_spacing(style.DEFAULT_SPACING) server_info = Gtk.Label( _("The server is the equivalent of what" " room you are in; people on the same server" " will be able to see each other, even when" " they aren't on the same network.")) server_info.set_alignment(0, 0) server_info.set_line_wrap(True) box_mesh.pack_start(server_info, False, True, 0) server_info.show() box_server = Gtk.HBox(spacing=style.DEFAULT_SPACING) label_server = Gtk.Label(label=_('Server:')) label_server.set_alignment(1, 0.5) label_server.modify_fg(Gtk.StateType.NORMAL, style.COLOR_SELECTION_GREY.get_gdk_color()) box_server.pack_start(label_server, False, True, 0) group.add_widget(label_server) label_server.show() self._entry = Gtk.Entry() self._entry.set_alignment(0) self._entry.set_size_request(int(Gdk.Screen.width() / 3), -1) box_server.pack_start(self._entry, False, True, 0) self._entry.show() box_mesh.pack_start(box_server, False, True, 0) box_server.show() social_help_info = Gtk.Label( _('Social Help is a forum that lets you connect with developers' ' and discuss Sugar Activities. Changing servers means' ' discussions will happen in a different place with' ' different people.')) social_help_info.set_alignment(0, 0) social_help_info.set_line_wrap(True) box_mesh.pack_start(social_help_info, False, True, 0) social_help_info.show() social_help_box = Gtk.HBox(spacing=style.DEFAULT_SPACING) social_help_label = Gtk.Label(label=_('Social Help Server:')) social_help_label.set_alignment(1, 0.5) social_help_label.modify_fg(Gtk.StateType.NORMAL, style.COLOR_SELECTION_GREY.get_gdk_color()) social_help_box.pack_start(social_help_label, False, True, 0) group.add_widget(social_help_label) social_help_label.show() self._social_help_entry = Gtk.Entry() self._social_help_entry.set_alignment(0) self._social_help_entry.set_size_request(int(Gdk.Screen.width() / 3), -1) social_help_box.pack_start(self._social_help_entry, False, True, 0) self._social_help_entry.show() box_mesh.pack_start(social_help_box, False, True, 0) social_help_box.show() workspace.pack_start(box_mesh, False, True, 0) box_mesh.show() separator_proxy = Gtk.HSeparator() workspace.pack_start(separator_proxy, False, False, 0) separator_proxy.show() self._add_proxy_section(workspace) self.setup() def _add_proxy_section(self, workspace): label_proxy = Gtk.Label(_('Proxy')) label_proxy.set_alignment(0, 0) workspace.pack_start(label_proxy, False, True, 0) label_proxy.show() box_proxy = Gtk.VBox() box_proxy.set_border_width(style.DEFAULT_SPACING * 2) box_proxy.set_spacing(style.DEFAULT_SPACING) workspace.pack_start(box_proxy, False, True, 0) box_proxy.show() self._proxy_alert = Alert() self._proxy_alert.props.title = _('Error') self._proxy_alert.props.msg = _('Proxy settings cannot be verified') box_proxy.pack_start(self._proxy_alert, False, False, 0) self._proxy_alert.connect('response', self._response_cb) self._proxy_alert.hide() # GSettings schemas for proxy: schemas = [ 'org.sugarlabs.system.proxy', 'org.sugarlabs.system.proxy.http', 'org.sugarlabs.system.proxy.https', 'org.sugarlabs.system.proxy.ftp', 'org.sugarlabs.system.proxy.socks' ] for schema in schemas: proxy_setting = Gio.Settings.new(schema) # We are not going to apply the settings immediatly. # We'll apply them if the user presses the "accept" # button, or we'll revert them if the user presses the # "cancel" button. proxy_setting.delay() alert = InlineAlert() self._proxy_settings[schema] = proxy_setting self._proxy_inline_alerts[schema] = alert size_group = Gtk.SizeGroup(Gtk.SizeGroupMode.HORIZONTAL) automatic_proxy_box = Gtk.VBox(spacing=style.DEFAULT_SPACING) manual_proxy_box = Gtk.VBox(spacing=style.DEFAULT_SPACING) option_sets = [('None', 'none', Gtk.VBox()), ('Use system proxy', 'system', Gtk.VBox()), ('Manual', 'manual', manual_proxy_box), ('Automatic', 'auto', automatic_proxy_box)] box_mode = ComboSettingBox( _('Method:'), self._proxy_settings['org.sugarlabs.system.proxy'], 'mode', option_sets, size_group) box_proxy.pack_start(box_mode, False, False, 0) box_mode.show() url_box = StringSettingBox( _('Configuration URL:'), self._proxy_settings['org.sugarlabs.system.proxy'], 'autoconfig-url', size_group) automatic_proxy_box.pack_start(url_box, True, True, 0) url_box.show() wpad_help_text = _('Web Proxy Autodiscovery is used when a' ' Configuration URL is not provided. This is not' ' recommended for untrusted public networks.') automatic_proxy_help = Gtk.Label(wpad_help_text) automatic_proxy_help.set_alignment(0, 0) automatic_proxy_help.set_line_wrap(True) automatic_proxy_help.show() automatic_proxy_box.pack_start(automatic_proxy_help, True, True, 0) # HTTP Section schema = 'org.sugarlabs.system.proxy.http' box_http = HostPortSettingBox(_('HTTP Proxy:'), self._proxy_inline_alerts[schema], self._proxy_settings[schema], size_group) manual_proxy_box.pack_start(box_http, False, False, 0) box_http.show() auth_contents_box = Gtk.VBox(spacing=style.DEFAULT_SPACING) auth_box = OptionalSettingsBox(_('Use authentication'), self._proxy_settings[schema], 'use-authentication', auth_contents_box) manual_proxy_box.pack_start(auth_box, False, False, 0) auth_box.show() proxy_http_setting = Gio.Settings.new(schema) proxy_http_setting.delay() box_username = StringSettingBox(_('Username:'******'authentication-user', size_group) auth_contents_box.pack_start(box_username, False, False, 0) box_username.show() box_password = StringSettingBox(_('Password:'******'authentication-password', size_group, password_field=True) auth_contents_box.pack_start(box_password, False, False, 0) box_password.show() # HTTPS Section schema = 'org.sugarlabs.system.proxy.https' box_https = HostPortSettingBox(_('HTTPS Proxy:'), self._proxy_inline_alerts[schema], self._proxy_settings[schema], size_group) manual_proxy_box.pack_start(box_https, False, False, 0) box_https.show() # FTP Section schema = 'org.sugarlabs.system.proxy.ftp' box_ftp = HostPortSettingBox(_('FTP Proxy:'), self._proxy_inline_alerts[schema], self._proxy_settings[schema], size_group) manual_proxy_box.pack_start(box_ftp, False, False, 0) box_ftp.show() # SOCKS Section schema = 'org.sugarlabs.system.proxy.socks' box_socks = HostPortSettingBox(_('SOCKS Proxy:'), self._proxy_inline_alerts[schema], self._proxy_settings[schema], size_group) manual_proxy_box.pack_start(box_socks, False, False, 0) box_socks.show() box_ignore = StringSettingBox_with_convert( _('Ignore Hosts:'), self._proxy_settings['org.sugarlabs.system.proxy'], 'ignore-hosts', type_as_to_string, string_to_type_as, size_group) manual_proxy_box.pack_start(box_ignore, False, False, 0) box_ignore.show() def setup(self): self._entry.set_text(self._start_jabber) self._social_help_entry.set_text(self._model.get_social_help()) try: radio_state = self._model.get_radio() except self._model.ReadError as detail: self._radio_alert.props.msg = detail self._radio_alert.show() else: self._button.set_active(radio_state) self._radio_valid = True self.needs_restart = False self._radio_change_handler = self._button.connect( 'toggled', self.__radio_toggled_cb) self._wireless_configuration_reset_handler = \ self._clear_wireless_button.connect( 'clicked', self.__wireless_configuration_reset_cb) def _response_cb(self, alert, response_id): if response_id is Gtk.ResponseType.APPLY: self._proxy_alert.hide() self._apply_proxy_settings() self.show_restart_alert = True self.emit('add-alert') elif response_id is Gtk.ResponseType.CANCEL: self.undo() self._proxy_alert.remove_button(Gtk.ResponseType.APPLY) self._proxy_alert.remove_button(Gtk.ResponseType.CANCEL) self._proxy_alert.hide() self.emit('set-toolbar-sensitivity', True) def _ping_servers(self): response_to_return = True non_blank_host_name_counter = 0 # To check accidental blank hostnames for schema in list(self._proxy_settings.keys()): if (schema != 'org.sugarlabs.system.proxy'): hostname = Gio.Settings.get_string( self._proxy_settings[schema], 'host') if hostname != '': non_blank_host_name_counter += 1 response = os.system("ping -c 1 -W 1 " + hostname) if (response): self._proxy_inline_alerts[schema].show() response_to_return = False if non_blank_host_name_counter == 0: response_to_return = False return response_to_return def _verify_settings(self): self._proxy_alert.props.title = _('Please Wait!') self._proxy_alert.props.msg = _('Proxy settings are being verified.') self._proxy_alert.show() flag_all_true = True g_proxy_schema = self._proxy_settings['org.sugarlabs.system.proxy'] g_mode = Gio.Settings.get_string(g_proxy_schema, 'mode') self.show_restart_alert = False if g_mode == 'auto': flag_all_true = os.path.isfile( Gio.Settings.get_string(g_proxy_schema, 'autoconfig-url')) elif g_mode == 'manual': flag_all_true = self._ping_servers() if flag_all_true: self.show_restart_alert = True self._proxy_alert.hide() self._apply_proxy_settings() else: self._proxy_alert.props.title = _('Error!') self._proxy_alert.props.msg = _('The following setting(s) seems ' 'to be incorrect and may break ' 'your internet connection') icon = Icon(icon_name='dialog-cancel') self._proxy_alert.add_button(Gtk.ResponseType.APPLY, _('Break my internet'), icon) icon.show() icon = Icon(icon_name='dialog-ok') self._proxy_alert.add_button(Gtk.ResponseType.CANCEL, _('Reset'), icon) icon.show() def _apply_proxy_settings(self): for setting in list(self._proxy_settings.values()): if (Gio.Settings.get_has_unapplied(setting)): setting.apply() def apply(self): self._apply_jabber(self._entry.get_text()) self._model.set_social_help(self._social_help_entry.get_text()) settings_changed = False for setting in list(self._proxy_settings.values()): if (Gio.Settings.get_has_unapplied(setting)): settings_changed = True if settings_changed: self.needs_restart = True self._is_cancellable = False self.restart_msg = _('Proxy changes require restart') self._verify_settings() else: self.show_restart_alert = True def undo(self): self._button.disconnect(self._radio_change_handler) self._radio_alert.hide() for setting in list(self._proxy_settings.values()): setting.revert() for alert in list(self._proxy_inline_alerts.values()): alert.hide() def _validate(self): if self._radio_valid: self.props.is_valid = True else: self.props.is_valid = False def __radio_toggled_cb(self, widget, data=None): radio_state = widget.get_active() try: self._model.set_radio(radio_state) except self._model.ReadError as detail: self._radio_alert.props.msg = detail self._radio_valid = False else: self._radio_valid = True if self._model.have_wireless_networks(): self._clear_wireless_button.set_sensitive(True) self._validate() return False def _apply_jabber(self, jabber): if jabber == self._model.get_jabber(): return self._model.set_jabber(jabber) def __wireless_configuration_reset_cb(self, widget): # FIXME: takes effect immediately, not after CP is closed with # confirmation button self._model.clear_wireless_networks() self._clear_wireless_button.set_sensitive(False)
class Download(object): def __init__(self, download, browser): self._download = download self._activity = browser.get_toplevel() self._source = download.get_uri() self._download.connect('notify::status', self.__state_change_cb) self._download.connect('error', self.__error_cb) self.datastore_deleted_handler = None self.dl_jobject = None self._object_id = None self._stop_alert = None self._progress = 0 self._last_update_progress = 0 self._progress_sid = None # figure out download URI self.temp_path = os.path.join(activity.get_activity_root(), 'instance') if not os.path.exists(self.temp_path): os.makedirs(self.temp_path) fd, self._dest_path = tempfile.mkstemp( dir=self.temp_path, suffix=download.get_suggested_filename(), prefix='tmp') os.close(fd) logging.debug('Download destination path: %s' % self._dest_path) # We have to start the download to get 'total-size' # property. It not, 0 is returned self._download.set_destination_uri('file://' + self._dest_path) self._download.start() def _update_progress(self): if self._progress > self._last_update_progress: self._last_update_progress = self._progress self.dl_jobject.metadata['progress'] = str(self._progress) datastore.write(self.dl_jobject) self._progress_sid = None return False def __progress_change_cb(self, download, something): self._progress = int(self._download.get_progress() * 100) if self._progress_sid is None: self._progress_sid = GObject.timeout_add( PROGRESS_TIMEOUT, self._update_progress) def __current_size_changed_cb(self, download, something): current_size = self._download.get_current_size() total_size = self._download.get_total_size() self._progress = int(current_size * 100 / total_size) if self._progress_sid is None: self._progress_sid = GObject.timeout_add( PROGRESS_TIMEOUT, self._update_progress) def __state_change_cb(self, download, gparamspec): state = self._download.get_status() if state == WebKit.DownloadStatus.STARTED: # Check free space and cancel the download if there is not enough. total_size = self._download.get_total_size() logging.debug('Total size of the file: %s', total_size) enough_space = self.enough_space( total_size, path=self.temp_path) if not enough_space: logging.debug('Download canceled because of Disk Space') self.cancel() self._canceled_alert = Alert() self._canceled_alert.props.title = _('Not enough space ' 'to download') total_size_mb = total_size / 1024.0 ** 2 free_space_mb = (self._free_available_space( path=self.temp_path) - SPACE_THRESHOLD) \ / 1024.0 ** 2 filename = self._download.get_suggested_filename() self._canceled_alert.props.msg = \ _('Download "%{filename}" requires %{total_size_in_mb}' ' MB of free space, only %{free_space_in_mb} MB' ' is available' % {'filename': filename, 'total_size_in_mb': format_float(total_size_mb), 'free_space_in_mb': format_float(free_space_mb)}) ok_icon = Icon(icon_name='dialog-ok') self._canceled_alert.add_button(Gtk.ResponseType.OK, _('Ok'), ok_icon) ok_icon.show() self._canceled_alert.connect('response', self.__stop_response_cb) self._activity.add_alert(self._canceled_alert) else: # FIXME: workaround for SL #4385 # self._download.connect('notify::progress', # self.__progress_change_cb) self._download.connect('notify::current-size', self.__current_size_changed_cb) self._create_journal_object() self._object_id = self.dl_jobject.object_id alert = TimeoutAlert(9) alert.props.title = _('Download started') alert.props.msg = _('%s' % self._download.get_suggested_filename()) self._activity.add_alert(alert) alert.connect('response', self.__start_response_cb) alert.show() global _active_downloads _active_downloads.append(self) elif state == WebKit.DownloadStatus.FINISHED: self._stop_alert = Alert() self._stop_alert.props.title = _('Download completed') self._stop_alert.props.msg = \ _('%s' % self._download.get_suggested_filename()) open_icon = Icon(icon_name='zoom-activity') self._stop_alert.add_button(Gtk.ResponseType.APPLY, _('Show in Journal'), open_icon) open_icon.show() ok_icon = Icon(icon_name='dialog-ok') self._stop_alert.add_button(Gtk.ResponseType.OK, _('Ok'), ok_icon) ok_icon.show() self._activity.add_alert(self._stop_alert) self._stop_alert.connect('response', self.__stop_response_cb) self._stop_alert.show() if self._progress_sid is not None: GObject.source_remove(self._progress_sid) self.dl_jobject.metadata['title'] = \ self._download.get_suggested_filename() self.dl_jobject.metadata['description'] = _('From: %s') \ % self._source self.dl_jobject.metadata['progress'] = '100' self.dl_jobject.file_path = self._dest_path # sniff for a mime type, no way to get headers from WebKit sniffed_mime_type = mime.get_for_file(self._dest_path) self.dl_jobject.metadata['mime_type'] = sniffed_mime_type if sniffed_mime_type in ('image/bmp', 'image/gif', 'image/jpeg', 'image/png', 'image/tiff'): preview = self._get_preview() if preview is not None: self.dl_jobject.metadata['preview'] = \ dbus.ByteArray(preview) datastore.write(self.dl_jobject, transfer_ownership=True, reply_handler=self.__internal_save_cb, error_handler=self.__internal_error_cb, timeout=360) elif state == WebKit.DownloadStatus.CANCELLED: self.cleanup() def __error_cb(self, download, err_code, err_detail, reason): logging.debug('Error downloading URI code %s, detail %s: %s' % (err_code, err_detail, reason)) def __internal_save_cb(self): logging.debug('Object saved succesfully to the datastore.') self.cleanup() def __internal_error_cb(self, err): logging.debug('Error saving activity object to datastore: %s' % err) self.cleanup() def __start_response_cb(self, alert, response_id): global _active_downloads if response_id is Gtk.ResponseType.CANCEL: logging.debug('Download Canceled') self.cancel() try: datastore.delete(self._object_id) except Exception, e: logging.warning('Object has been deleted already %s' % e) self.cleanup() if self._stop_alert is not None: self._activity.remove_alert(self._stop_alert) self._activity.remove_alert(alert)
class Download(object): def __init__(self, download, browser): self._download = download self._activity = browser.get_toplevel() self._source = download.get_uri() self._download.connect('notify::status', self.__state_change_cb) self._download.connect('error', self.__error_cb) self.datastore_deleted_handler = None self.dl_jobject = None self._object_id = None self._last_update_time = 0 self._last_update_percent = 0 self._stop_alert = None # figure out download URI self.temp_path = os.path.join(activity.get_activity_root(), 'instance') if not os.path.exists(self.temp_path): os.makedirs(self.temp_path) fd, self._dest_path = tempfile.mkstemp( dir=self.temp_path, suffix=download.get_suggested_filename(), prefix='tmp') os.close(fd) logging.debug('Download destination path: %s' % self._dest_path) # We have to start the download to get 'total-size' # property. It not, 0 is returned self._download.set_destination_uri('file://' + self._dest_path) self._download.start() def __progress_change_cb(self, download, something): progress = self._download.get_progress() self.dl_jobject.metadata['progress'] = str(int(progress * 100)) datastore.write(self.dl_jobject) def __state_change_cb(self, download, gparamspec): state = self._download.get_status() if state == WebKit.DownloadStatus.STARTED: # Check free space and cancel the download if there is not enough. total_size = self._download.get_total_size() logging.debug('Total size of the file: %s', total_size) enough_space = self.enough_space(total_size, path=self.temp_path) if not enough_space: logging.debug('Download canceled because of Disk Space') self.cancel() self._canceled_alert = Alert() self._canceled_alert.props.title = _('Not enough space ' 'to download') total_size_mb = total_size / 1024.0**2 free_space_mb = self._free_available_space( path=self.temp_path) - SPACE_THRESHOLD / 1024.0**2 filename = self._download.get_suggested_filename() self._canceled_alert.props.msg = \ _('Download "%{filename}" requires %{total_size_in_mb}' ' MB of free space, only %{free_space_in_mb} MB' ' is available' % {'filename': filename, 'total_size_in_mb': format_float(total_size_mb), 'free_space_in_mb': format_float(free_space_mb)}) ok_icon = Icon(icon_name='dialog-ok') self._canceled_alert.add_button(Gtk.ResponseType.OK, _('Ok'), ok_icon) ok_icon.show() self._canceled_alert.connect('response', self.__stop_response_cb) self._activity.add_alert(self._canceled_alert) else: self._download.connect('notify::progress', self.__progress_change_cb) self._create_journal_object() self._object_id = self.dl_jobject.object_id alert = TimeoutAlert(9) alert.props.title = _('Download started') alert.props.msg = _('%s' % self._download.get_suggested_filename()) self._activity.add_alert(alert) alert.connect('response', self.__start_response_cb) alert.show() global _active_downloads _active_downloads.append(self) elif state == WebKit.DownloadStatus.FINISHED: self._stop_alert = Alert() self._stop_alert.props.title = _('Download completed') self._stop_alert.props.msg = \ _('%s' % self._download.get_suggested_filename()) open_icon = Icon(icon_name='zoom-activity') self._stop_alert.add_button(Gtk.ResponseType.APPLY, _('Show in Journal'), open_icon) open_icon.show() ok_icon = Icon(icon_name='dialog-ok') self._stop_alert.add_button(Gtk.ResponseType.OK, _('Ok'), ok_icon) ok_icon.show() self._activity.add_alert(self._stop_alert) self._stop_alert.connect('response', self.__stop_response_cb) self._stop_alert.show() if self._dest_path.endswith('.journal'): metadata, preview_data, file_path = \ utils.unpackage_ds_object(self._dest_path) original_object_id = metadata['original_object_id'] for key in metadata.keys(): self.dl_jobject.metadata[key] = metadata[key] self.dl_jobject.metadata['preview'] = dbus.ByteArray( preview_data) self.dl_jobject.file_path = file_path datastore.write(self.dl_jobject, transfer_ownership=True, reply_handler=self.__internal_save_cb, error_handler=self.__internal_error_cb, timeout=360) # notify to the server, the object was successfully downloaded url = 'ws://%s:%d/websocket' % (self._activity.ip, self._activity.port) messanger = utils.Messanger(url) data = utils.get_user_data() data['object_id'] = original_object_id messanger.send_message('DOWNLOADED', data) else: self.dl_jobject.metadata['title'] = \ self._download.get_suggested_filename() self.dl_jobject.metadata['description'] = _('From: %s') \ % self._source self.dl_jobject.metadata['progress'] = '100' self.dl_jobject.file_path = self._dest_path # sniff for a mime type, no way to get headers from WebKit sniffed_mime_type = mime.get_for_file(self._dest_path) self.dl_jobject.metadata['mime_type'] = sniffed_mime_type datastore.write(self.dl_jobject, transfer_ownership=True, reply_handler=self.__internal_save_cb, error_handler=self.__internal_error_cb, timeout=360) elif state == WebKit.DownloadStatus.CANCELLED: self.cleanup() def __error_cb(self, download, err_code, err_detail, reason): logging.debug('Error downloading URI code %s, detail %s: %s' % (err_code, err_detail, reason)) def __internal_save_cb(self): logging.debug('Object saved succesfully to the datastore.') self.cleanup() def __internal_error_cb(self, err): logging.debug('Error saving activity object to datastore: %s' % err) self.cleanup() def __start_response_cb(self, alert, response_id): global _active_downloads if response_id is Gtk.ResponseType.CANCEL: logging.debug('Download Canceled') self.cancel() try: datastore.delete(self._object_id) except Exception, e: logging.warning('Object has been deleted already %s' % e) self.cleanup() if self._stop_alert is not None: self._activity.remove_alert(self._stop_alert) self._activity.remove_alert(alert)
class Download(object): def __init__(self, webkit_download, activity): self._download = webkit_download self._activity = activity self._source = self._download.get_request().get_uri() logging.debug('START Download %s', self._source) self.datastore_deleted_handler = None self.dl_jobject = None self._object_id = None self._start_alert = None self._stop_alert = None self._dest_path = '' self._progress = 0 self._last_update_progress = 0 self._progress_sid = None self.temp_path = os.path.join(self._activity.get_activity_root(), 'instance') if not os.path.exists(self.temp_path): os.makedirs(self.temp_path) self._download.connect('failed', self.__download_failed_cb) self._download.connect('finished', self.__download_finished_cb) self._download.connect('received-data', self.__download_received_data_cb) # Notify response is called before decide destination self._download.connect('notify::response', self.__notify_response_cb) self._download.connect('decide-destination', self.__decide_destination_cb) self._download.connect('created-destination', self.__created_destination_cb) def __notify_response_cb(self, download, pspec): logging.debug('__notify_response_cb') response = download.get_response() # Check free space and cancel the download if there is not enough. total_size = response.get_content_length() logging.debug('Total size of the file: %s', total_size) enough_space = self.enough_space(total_size, path=self.temp_path) if not enough_space: logging.debug('Download canceled because of Disk Space') self.cancel() self._canceled_alert = Alert() self._canceled_alert.props.title = _('Not enough space ' 'to download') total_size_mb = total_size / 1024.0**2 free_space_mb = (self._free_available_space( path=self.temp_path) - SPACE_THRESHOLD) \ / 1024.0 ** 2 filename = response.get_suggested_filename() self._canceled_alert.props.msg = \ _('Download "%{filename}" requires %{total_size_in_mb}' ' MB of free space, only %{free_space_in_mb} MB' ' is available' % {'filename': filename, 'total_size_in_mb': format_float(total_size_mb), 'free_space_in_mb': format_float(free_space_mb)}) ok_icon = Icon(icon_name='dialog-ok') self._canceled_alert.add_button(Gtk.ResponseType.OK, _('Ok'), ok_icon) ok_icon.show() self._canceled_alert.connect('response', self.__canceled_response_cb) self._activity.add_alert(self._canceled_alert) def __canceled_response_cb(self, alert, response_id): self._activity.remove_alert(alert) def __decide_destination_cb(self, download, suggested_filename): logging.debug('__decide_desintation_cb suggests %s', suggested_filename) self._start_alert = TimeoutAlert(9) self._start_alert.props.title = _('Downloading') self._start_alert.props.msg = suggested_filename self._activity.add_alert(self._start_alert) self._start_alert.connect('response', self.__start_response_cb) self._start_alert.show() self._suggested_filename = suggested_filename # figure out download URI self._dest_path = tempfile.mktemp(dir=self.temp_path, suffix=suggested_filename, prefix='tmp') logging.debug('Download destination path: %s' % self._dest_path) self._download.set_destination('file://' + self._dest_path) logging.error(self._download.get_destination) return True def __created_destination_cb(self, download, dest): logging.debug('__created_destination_cb at %s', dest) self._create_journal_object() self._object_id = self.dl_jobject.object_id def _update_progress(self): if self._progress > self._last_update_progress: self._last_update_progress = self._progress self.dl_jobject.metadata['progress'] = str(self._progress) datastore.write(self.dl_jobject) self._progress_sid = None return False def __download_received_data_cb(self, download, data_size): self._progress = int(self._download.get_estimated_progress() * 100) if self._progress_sid is None: self._progress_sid = GObject.timeout_add(PROGRESS_TIMEOUT, self._update_progress) def __download_finished_cb(self, download): if hasattr(self._activity, 'busy'): self._activity.unbusy() if self._progress_sid is not None: GObject.source_remove(self._progress_sid) if self.dl_jobject is None: return # the "failed" signal was observed self.dl_jobject.metadata['title'] = self._suggested_filename self.dl_jobject.metadata['description'] = _('From: %s') \ % self._source self.dl_jobject.metadata['progress'] = '100' self.dl_jobject.file_path = self._dest_path mime_type = Gio.content_type_guess(self._dest_path)[0] if mime_type != 'application/vnd.olpc-sugar': mime_type = download.get_response().get_mime_type() self.dl_jobject.metadata['mime_type'] = mime_type if mime_type in ('image/bmp', 'image/gif', 'image/jpeg', 'image/png', 'image/tiff'): preview = self._get_preview() if preview is not None: self.dl_jobject.metadata['preview'] = \ dbus.ByteArray(preview) datastore.write(self.dl_jobject, transfer_ownership=True, reply_handler=self.__internal_save_cb, error_handler=self.__internal_error_cb, timeout=360) if self._start_alert is not None: self._activity.remove_alert(self._start_alert) self._stop_alert = Alert() self._stop_alert.props.title = _('Downloaded') self._stop_alert.props.msg = self._suggested_filename bundle = None if _HAS_BUNDLE_LAUNCHER: bundle = get_bundle(object_id=self._object_id) if bundle is not None: icon = Icon(file=bundle.get_icon()) label = _('Open with %s') % bundle.get_name() response_id = Gtk.ResponseType.APPLY else: icon = Icon(icon_name='zoom-activity') label = _('Show in Journal') response_id = Gtk.ResponseType.ACCEPT self._stop_alert.add_button(response_id, label, icon) icon.show() ok_icon = Icon(icon_name='dialog-ok') self._stop_alert.add_button(Gtk.ResponseType.OK, _('Ok'), ok_icon) ok_icon.show() self._activity.add_alert(self._stop_alert) self._stop_alert.connect('response', self.__stop_response_cb) self._stop_alert.show() def __download_failed_cb(self, download, error): logging.error('Error downloading URI due to %s' % error) self.cleanup() def __internal_save_cb(self): logging.debug('Object saved succesfully to the datastore.') self.cleanup() def __internal_error_cb(self, err): logging.debug('Error saving activity object to datastore: %s' % err) self.cleanup() def __start_response_cb(self, alert, response_id): if response_id is Gtk.ResponseType.CANCEL: logging.debug('Download Canceled') self.cancel() try: datastore.delete(self._object_id) except Exception as e: logging.warning('Object has been deleted already %s' % e) self.cleanup() if self._stop_alert is not None: self._activity.remove_alert(self._stop_alert) self._activity.remove_alert(alert) self._start_alert = None def __stop_response_cb(self, alert, response_id): if response_id == Gtk.ResponseType.APPLY: launch_bundle(object_id=self._object_id) if response_id == Gtk.ResponseType.ACCEPT: activity.show_object_in_journal(self._object_id) self._activity.remove_alert(alert) def cleanup(self): global _active_downloads if self in _active_downloads: _active_downloads.remove(self) if self.datastore_deleted_handler is not None: self.datastore_deleted_handler.remove() self.datastore_deleted_handler = None if os.path.isfile(self._dest_path): os.remove(self._dest_path) if self.dl_jobject is not None: self.dl_jobject.destroy() self.dl_jobject = None def cancel(self): self._download.cancel() def enough_space(self, size, path='/'): """Check if there is enough (size) free space on path size -- free space requested in Bytes path -- device where the check will be done. For example: '/tmp' This method is useful to check the free space, for example, before starting a download from internet, creating a big map in some game or whatever action that needs some space in the Hard Disk. """ free_space = self._free_available_space(path=path) return free_space - size > SPACE_THRESHOLD def _free_available_space(self, path='/'): """Return available space in Bytes This method returns the available free space in the 'path' and returns this amount in Bytes. """ s = os.statvfs(path) return s.f_bavail * s.f_frsize def _create_journal_object(self): logging.debug('_create_journal_object') self.dl_jobject = datastore.create() filename = self._download.get_response().get_suggested_filename() self.dl_jobject.metadata['title'] = \ _('Downloading %(filename)s from \n%(source)s.') % \ {'filename': filename, 'source': self._source} self.dl_jobject.metadata['progress'] = '0' self.dl_jobject.metadata['keep'] = '0' self.dl_jobject.metadata['buddies'] = '' self.dl_jobject.metadata['preview'] = '' self.dl_jobject.metadata['icon-color'] = \ profile.get_color().to_string() self.dl_jobject.metadata['mime_type'] = '' self.dl_jobject.file_path = self._dest_path datastore.write(self.dl_jobject) bus = dbus.SessionBus() obj = bus.get_object(DS_DBUS_SERVICE, DS_DBUS_PATH) datastore_dbus = dbus.Interface(obj, DS_DBUS_INTERFACE) self.datastore_deleted_handler = datastore_dbus.connect_to_signal( 'Deleted', self.__datastore_deleted_cb, arg0=self.dl_jobject.object_id) def _get_preview(self): # This code borrows from sugar3.activity.Activity.get_preview # to make the preview with cairo, and also uses GdkPixbuf to # load any GdkPixbuf supported format. pixbuf = GdkPixbuf.Pixbuf.new_from_file(self._dest_path) image_width = pixbuf.get_width() image_height = pixbuf.get_height() preview_width, preview_height = activity.PREVIEW_SIZE preview_surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, preview_width, preview_height) cr = cairo.Context(preview_surface) scale_w = preview_width * 1.0 / image_width scale_h = preview_height * 1.0 / image_height scale = min(scale_w, scale_h) translate_x = int((preview_width - (image_width * scale)) / 2) translate_y = int((preview_height - (image_height * scale)) / 2) cr.translate(translate_x, translate_y) cr.scale(scale, scale) cr.set_source_rgba(1, 1, 1, 0) cr.set_operator(cairo.OPERATOR_SOURCE) cr.paint() Gdk.cairo_set_source_pixbuf(cr, pixbuf, 0, 0) cr.paint() preview_str = io.BytesIO() preview_surface.write_to_png(preview_str) return preview_str.getvalue() def __datastore_deleted_cb(self, uid): logging.debug( 'Downloaded entry has been deleted' ' from the datastore: %r', uid) global _active_downloads if self in _active_downloads: self.cancel() self.cleanup()
class Download(object): def __init__(self, download, browser): self._download = download self._activity = browser.get_toplevel() self._source = download.get_uri() self._download.connect('notify::status', self.__state_change_cb) self._download.connect('error', self.__error_cb) self.datastore_deleted_handler = None self.dl_jobject = None self._object_id = None self._last_update_time = 0 self._last_update_percent = 0 self._stop_alert = None # figure out download URI self.temp_path = os.path.join(activity.get_activity_root(), 'instance') if not os.path.exists(self.temp_path): os.makedirs(self.temp_path) fd, self._dest_path = tempfile.mkstemp( dir=self.temp_path, suffix=download.get_suggested_filename(), prefix='tmp') os.close(fd) logging.debug('Download destination path: %s' % self._dest_path) # We have to start the download to get 'total-size' # property. It not, 0 is returned self._download.set_destination_uri('file://' + self._dest_path) self._download.start() def __progress_change_cb(self, download, something): progress = self._download.get_progress() self.dl_jobject.metadata['progress'] = str(int(progress * 100)) datastore.write(self.dl_jobject) def __state_change_cb(self, download, gparamspec): state = self._download.get_status() if state == WebKit.DownloadStatus.STARTED: # Check free space and cancel the download if there is not enough. total_size = self._download.get_total_size() logging.debug('Total size of the file: %s', total_size) enough_space = self.enough_space( total_size, path=self.temp_path) if not enough_space: logging.debug('Download canceled because of Disk Space') self.cancel() self._canceled_alert = Alert() self._canceled_alert.props.title = _('Not enough space ' 'to download') total_size_mb = total_size / 1024.0 ** 2 free_space_mb = self._free_available_space( path=self.temp_path) - SPACE_THRESHOLD / 1024.0 ** 2 filename = self._download.get_suggested_filename() self._canceled_alert.props.msg = \ _('Download "%{filename}" requires %{total_size_in_mb}' ' MB of free space, only %{free_space_in_mb} MB' ' is available' % {'filename': filename, 'total_size_in_mb': format_float(total_size_mb), 'free_space_in_mb': format_float(free_space_mb)}) ok_icon = Icon(icon_name='dialog-ok') self._canceled_alert.add_button(Gtk.ResponseType.OK, _('Ok'), ok_icon) ok_icon.show() self._canceled_alert.connect('response', self.__stop_response_cb) self._activity.add_alert(self._canceled_alert) else: self._download.connect('notify::progress', self.__progress_change_cb) self._create_journal_object() self._object_id = self.dl_jobject.object_id alert = TimeoutAlert(9) alert.props.title = _('Download started') alert.props.msg = _('%s' % self._download.get_suggested_filename()) self._activity.add_alert(alert) alert.connect('response', self.__start_response_cb) alert.show() global _active_downloads _active_downloads.append(self) elif state == WebKit.DownloadStatus.FINISHED: self._stop_alert = Alert() self._stop_alert.props.title = _('Download completed') self._stop_alert.props.msg = \ _('%s' % self._download.get_suggested_filename()) open_icon = Icon(icon_name='zoom-activity') self._stop_alert.add_button(Gtk.ResponseType.APPLY, _('Show in Journal'), open_icon) open_icon.show() ok_icon = Icon(icon_name='dialog-ok') self._stop_alert.add_button(Gtk.ResponseType.OK, _('Ok'), ok_icon) ok_icon.show() self._activity.add_alert(self._stop_alert) self._stop_alert.connect('response', self.__stop_response_cb) self._stop_alert.show() if self._dest_path.endswith('.journal'): metadata, preview_data, file_path = \ utils.unpackage_ds_object(self._dest_path) original_object_id = metadata['original_object_id'] for key in metadata.keys(): self.dl_jobject.metadata[key] = metadata[key] self.dl_jobject.metadata['preview'] = dbus.ByteArray( preview_data) self.dl_jobject.file_path = file_path datastore.write(self.dl_jobject, transfer_ownership=True, reply_handler=self.__internal_save_cb, error_handler=self.__internal_error_cb, timeout=360) # notify to the server, the object was successfully downloaded url = 'ws://%s:%d/websocket' % (self._activity.ip, self._activity.port) messanger = utils.Messanger(url) data = utils.get_user_data() data['object_id'] = original_object_id messanger.send_message('DOWNLOADED', data) else: self.dl_jobject.metadata['title'] = \ self._download.get_suggested_filename() self.dl_jobject.metadata['description'] = _('From: %s') \ % self._source self.dl_jobject.metadata['progress'] = '100' self.dl_jobject.file_path = self._dest_path # sniff for a mime type, no way to get headers from WebKit sniffed_mime_type = mime.get_for_file(self._dest_path) self.dl_jobject.metadata['mime_type'] = sniffed_mime_type datastore.write(self.dl_jobject, transfer_ownership=True, reply_handler=self.__internal_save_cb, error_handler=self.__internal_error_cb, timeout=360) elif state == WebKit.DownloadStatus.CANCELLED: self.cleanup() def __error_cb(self, download, err_code, err_detail, reason): logging.debug('Error downloading URI code %s, detail %s: %s' % (err_code, err_detail, reason)) def __internal_save_cb(self): logging.debug('Object saved succesfully to the datastore.') self.cleanup() def __internal_error_cb(self, err): logging.debug('Error saving activity object to datastore: %s' % err) self.cleanup() def __start_response_cb(self, alert, response_id): global _active_downloads if response_id is Gtk.ResponseType.CANCEL: logging.debug('Download Canceled') self.cancel() try: datastore.delete(self._object_id) except Exception, e: logging.warning('Object has been deleted already %s' % e) self.cleanup() if self._stop_alert is not None: self._activity.remove_alert(self._stop_alert) self._activity.remove_alert(alert)
class Download(object): def __init__(self, download, browser): self._download = download self._activity = browser.get_toplevel() self._source = download.get_uri() self._download.connect('notify::progress', self.__progress_change_cb) self._download.connect('notify::status', self.__state_change_cb) self._download.connect('error', self.__error_cb) self.datastore_deleted_handler = None self.dl_jobject = None self._object_id = None self._last_update_time = 0 self._last_update_percent = 0 self._stop_alert = None # figure out download URI temp_path = os.path.join(activity.get_activity_root(), 'instance') if not os.path.exists(temp_path): os.makedirs(temp_path) fd, self._dest_path = tempfile.mkstemp( dir=temp_path, suffix=download.get_suggested_filename(), prefix='tmp') os.close(fd) logging.debug('Download destination path: %s' % self._dest_path) self._download.set_destination_uri('file://' + self._dest_path) self._download.start() def __progress_change_cb(self, download, something): progress = self._download.get_progress() self.dl_jobject.metadata['progress'] = str(int(progress * 100)) datastore.write(self.dl_jobject) def __state_change_cb(self, download, gparamspec): state = self._download.get_status() if state == WebKit.DownloadStatus.STARTED: self._create_journal_object() self._object_id = self.dl_jobject.object_id alert = TimeoutAlert(9) alert.props.title = _('Download started') alert.props.msg = _('%s' % self._download.get_suggested_filename()) self._activity.add_alert(alert) alert.connect('response', self.__start_response_cb) alert.show() global _active_downloads _active_downloads.append(self) elif state == WebKit.DownloadStatus.FINISHED: self._stop_alert = Alert() self._stop_alert.props.title = _('Download completed') self._stop_alert.props.msg = \ _('%s' % self._download.get_suggested_filename()) open_icon = Icon(icon_name='zoom-activity') self._stop_alert.add_button(Gtk.ResponseType.APPLY, _('Show in Journal'), open_icon) open_icon.show() ok_icon = Icon(icon_name='dialog-ok') self._stop_alert.add_button(Gtk.ResponseType.OK, _('Ok'), ok_icon) ok_icon.show() self._activity.add_alert(self._stop_alert) self._stop_alert.connect('response', self.__stop_response_cb) self._stop_alert.show() self.dl_jobject.metadata['title'] = \ self._download.get_suggested_filename() self.dl_jobject.metadata['description'] = _('From: %s') \ % self._source self.dl_jobject.metadata['progress'] = '100' self.dl_jobject.file_path = self._dest_path # sniff for a mime type, no way to get headers from WebKit sniffed_mime_type = mime.get_for_file(self._dest_path) self.dl_jobject.metadata['mime_type'] = sniffed_mime_type datastore.write(self.dl_jobject, transfer_ownership=True, reply_handler=self.__internal_save_cb, error_handler=self.__internal_error_cb, timeout=360) elif state == WebKit.DownloadStatus.CANCELLED: self.cleanup() def __error_cb(self, download, err_code, err_detail, reason): logging.debug('Error downloading URI code %s, detail %s: %s' % (err_code, err_detail, reason)) def __internal_save_cb(self): logging.debug('Object saved succesfully to the datastore.') self.cleanup() def __internal_error_cb(self, err): logging.debug('Error saving activity object to datastore: %s' % err) self.cleanup() def __start_response_cb(self, alert, response_id): global _active_downloads if response_id is Gtk.ResponseType.CANCEL: logging.debug('Download Canceled') self.cancel() try: datastore.delete(self._object_id) except Exception, e: logging.warning('Object has been deleted already %s' % e) self.cleanup() if self._stop_alert is not None: self._activity.remove_alert(self._stop_alert) self._activity.remove_alert(alert)
class StoryActivity(activity.Activity): ''' Storytelling game ''' def __init__(self, handle): ''' Initialize the toolbars and the game board ''' try: super(StoryActivity, self).__init__(handle) except dbus.exceptions.DBusException as e: _logger.error(str(e)) self._path = activity.get_bundle_path() self.datapath = os.path.join(activity.get_activity_root(), 'instance') self._nick = profile.get_nick_name() if profile.get_color() is not None: self._colors = profile.get_color().to_string().split(',') else: self._colors = ['#A0FFA0', '#FF8080'] self._old_cursor = self.get_window().get_cursor() self.tablet_mode = _is_tablet_mode() self.recording = False self.audio_process = None self._arecord = None self._alert = None self._uid = None self._setup_toolbars() self._setup_dispatch_table() self._fixed = Gtk.Fixed() self._fixed.connect('size-allocate', self._fixed_resize_cb) self._fixed.show() self.set_canvas(self._fixed) self._vbox = Gtk.VBox(False, 0) self._vbox.set_size_request(Gdk.Screen.width(), Gdk.Screen.height()) self._fixed.put(self._vbox, 0, 0) self._vbox.show() self._canvas = Gtk.DrawingArea() self._canvas.set_size_request(int(Gdk.Screen.width()), int(Gdk.Screen.height())) self._canvas.show() self._vbox.pack_end(self._canvas, True, True, 0) self._vbox.show() entry_width = Gdk.Screen.width() - 6 * style.GRID_CELL_SIZE entry_height = 3 * style.GRID_CELL_SIZE self._entry = Gtk.TextView() self._entry.set_wrap_mode(Gtk.WrapMode.WORD) self._entry.set_pixels_above_lines(0) self._entry.set_top_margin(10) self._entry.set_bottom_margin(10) self._entry.set_right_margin(10) self._entry.set_left_margin(10) self._entry.set_size_request(entry_width, entry_height) font_desc = Pango.font_description_from_string('14') self._entry.modify_font(font_desc) self.text_buffer = self._entry.get_buffer() self.text_buffer.set_text(PLACEHOLDER) self._entry.connect('focus-in-event', self._text_focus_in_cb) self._entry.connect('key-press-event', self._text_focus_in_cb) self._entry.connect('focus-out-event', self._text_focus_out_cb) self._entry.get_buffer().connect('changed', self._text_changed_cb) self._entry.show() self._scrolled_window = Gtk.ScrolledWindow() self._scrolled_window.set_shadow_type(Gtk.ShadowType.ETCHED_IN) self._scrolled_window.set_size_request( Gdk.Screen.width() - 6 * style.GRID_CELL_SIZE, style.GRID_CELL_SIZE * 3) self._scrolled_window.set_border_width(5) self._scrolled_window.add(self._entry) if self.tablet_mode: self._fixed.put(self._scrolled_window, 3 * style.GRID_CELL_SIZE, style.DEFAULT_SPACING) else: self._fixed.put( self._scrolled_window, 3 * style.GRID_CELL_SIZE, Gdk.Screen.height() - style.DEFAULT_SPACING - style.GRID_CELL_SIZE * 4) self._scrolled_window.show() self._fixed.show() self._game = Game(self._canvas, parent=self, path=self._path, root=activity.get_bundle_path(), colors=self._colors) self._setup_presence_service() if 'mode' in self.metadata: self._game.set_mode(self.metadata['mode']) if self.metadata['mode'] == 'array': self.array_button.set_active(True) self.autoplay_button.set_sensitive(False) else: self._linear_button.set_active(True) if 'uid' in self.metadata: self._uid = self.metadata['uid'] else: self._uid = generate_uid() self.metadata['uid'] = self._uid if 'dotlist' in self.metadata: self._restore() self.check_audio_status() self.check_text_status() else: self._game.new_game() Gdk.Screen.get_default().connect('size-changed', self._configure_cb) def close(self, **kwargs): aplay.close() activity.Activity.close(self, **kwargs) def _configure_cb(self, event): self._canvas.set_size_request(int(Gdk.Screen.width()), int(Gdk.Screen.height())) self._vbox.set_size_request(Gdk.Screen.width(), Gdk.Screen.height()) entry_width = Gdk.Screen.width() - 6 * style.GRID_CELL_SIZE entry_height = 3 * style.GRID_CELL_SIZE self._entry.set_size_request(entry_width, entry_height) self._scrolled_window.set_size_request( Gdk.Screen.width() - 6 * style.GRID_CELL_SIZE, style.GRID_CELL_SIZE * 3) if not self.tablet_mode: self._fixed.move( self._scrolled_window, 3 * style.GRID_CELL_SIZE, Gdk.Screen.height() - style.DEFAULT_SPACING - style.GRID_CELL_SIZE * 4) self._game.configure() def _restore_cursor(self): ''' No longer waiting, so restore standard cursor. ''' if not hasattr(self, 'get_window'): return self.get_window().set_cursor(self._old_cursor) def _waiting_cursor(self): ''' Waiting, so set watch cursor. ''' if not hasattr(self, 'get_window'): return self._old_cursor = self.get_window().get_cursor() self.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH)) def _text_changed_cb(self, text_buffer): self._entry.place_cursor_onscreen() def _fixed_resize_cb(self, widget=None, rect=None): ''' If a toolbar opens or closes, we need to resize the vbox holding out scrolling window. ''' self._vbox.set_size_request(rect.width, rect.height) def _text_focus_in_cb(self, widget=None, event=None): bounds = self.text_buffer.get_bounds() text = self.text_buffer.get_text(bounds[0], bounds[1], True) if text in [PLACEHOLDER, PLACEHOLDER1, PLACEHOLDER2]: self.text_buffer.set_text('') self.metadata['dirty'] = 'True' self._game.set_speak_icon_state(True) if self._game.playing: self._game.stop() def _text_focus_out_cb(self, widget=None, event=None): self.speak_text_cb() def speak_text_cb(self, button=None): bounds = self.text_buffer.get_bounds() text = self.text_buffer.get_text(bounds[0], bounds[1], True) if self._game.get_mode() == 'array': if text != PLACEHOLDER: self.metadata['text'] = text self.metadata['dirty'] = 'True' self._game.set_speak_icon_state(True) else: self._game.set_speak_icon_state(False) else: if text not in [PLACEHOLDER, PLACEHOLDER1, PLACEHOLDER2]: key = 'text-%d' % self._game.current_image self.metadata[key] = text self.metadata['dirty'] = 'True' self._game.set_speak_icon_state(True) else: self._game.set_speak_icon_state(False) def check_text_status(self): self._game.set_speak_icon_state(False) if self._game.get_mode() == 'array': if 'text' in self.metadata: self.text_buffer.set_text(self.metadata['text']) if len(self.metadata['text']) > 0: self.metadata['dirty'] = 'True' self._game.set_speak_icon_state(True) else: self.text_buffer.set_text(PLACEHOLDER) else: self.text_buffer.set_text(PLACEHOLDER) else: key = 'text-%d' % self._game.current_image if key in self.metadata: self.text_buffer.set_text(self.metadata[key]) if len(self.metadata[key]) > 0: self.metadata['dirty'] = 'True' self._game.set_speak_icon_state(True) elif self._game.current_image == 0: self.text_buffer.set_text(PLACEHOLDER1) else: self.text_buffer.set_text(PLACEHOLDER2) elif self._game.current_image == 0: self.text_buffer.set_text(PLACEHOLDER1) else: self.text_buffer.set_text(PLACEHOLDER2) def _clear_text(self): if 'text' in self.metadata: self.metadata['text'] = '' for i in range(9): if 'text-%d' % i in self.metadata: self.metadata['text-%d' % i] = '' if 'dirty' in self.metadata: self.metadata['dirty'] = 'False' if self._game.get_mode() == 'array': self.text_buffer.set_text(PLACEHOLDER) elif self._game.current_image == 0: self.text_buffer.set_text(PLACEHOLDER1) else: self.text_buffer.set_text(PLACEHOLDER2) self._game.set_speak_icon_state(False) def _clear_audio_notes(self): dsobject = self._search_for_audio_note(self._uid, target=self._uid) if dsobject is not None: dsobject.metadata['tags'] = '' datastore.write(dsobject) dsobject.destroy() for i in range(9): target = '%s-%d' % (self._uid, i) dsobject = self._search_for_audio_note(self._uid, target=target) if dsobject is not None: dsobject.metadata['tags'] = '' datastore.write(dsobject) dsobject.destroy() self._game.set_play_icon_state(False) def check_audio_status(self): if self._search_for_audio_note(self._uid): self._game.set_play_icon_state(True) else: self._game.set_play_icon_state(False) def _setup_toolbars(self): ''' Setup the toolbars. ''' self.max_participants = 1 # collaboration is unfinished toolbox = ToolbarBox() # Activity toolbar activity_button = ActivityToolbarButton(self) toolbox.toolbar.insert(activity_button, 0) activity_button.show() self.set_toolbar_box(toolbox) toolbox.show() self.toolbar = toolbox.toolbar self._new_game_button_h = button_factory('view-refresh', self.toolbar, self._new_game_cb, tooltip=_('Load new images')) self.array_button = radio_factory('array', self.toolbar, self._array_cb, tooltip=_('View images all at once'), group=None) self._linear_button = radio_factory( 'linear', self.toolbar, self._linear_cb, tooltip=_('View images one at a time'), group=self.array_button) self.autoplay_button = button_factory('media-playback-start', self.toolbar, self._do_autoplay_cb, tooltip=_('Play')) self.autoplay_button.set_sensitive(False) separator_factory(self.toolbar) self.save_as_image = button_factory('image-saveoff', self.toolbar, self._do_save_as_image_cb, tooltip=_('Save as image')) self.save_as_pdf = button_factory('save-as-pdf', self.toolbar, self._do_save_as_pdf_cb, tooltip=_('Save as PDF')) separator_factory(toolbox.toolbar, True, False) stop_button = StopButton(self) stop_button.props.accelerator = '<Ctrl>q' toolbox.toolbar.insert(stop_button, -1) stop_button.show() def _do_autoplay_cb(self, button=None): if self._game.playing: self._game.stop() else: self.autoplay_button.set_icon_name('media-playback-pause') self.autoplay_button.set_tooltip(_('Pause')) self.array_button.set_sensitive(False) self._game.autoplay() def _array_cb(self, button=None): self.speak_text_cb() self._game.set_mode('array') self.autoplay_button.set_sensitive(False) if self._uid is not None: self.check_audio_status() self.check_text_status() def _linear_cb(self, button=None): self.speak_text_cb() self._game.set_mode('linear') self.autoplay_button.set_sensitive(True) if self._uid is not None: self.check_audio_status() self.check_text_status() def _new_game_cb(self, button=None): ''' Start a new game. ''' if 'dirty' in self.metadata and self.metadata['dirty'] == 'True': if self._alert is not None: self.remove_alert(self._alert) self._alert = None self._alert = ConfirmationAlert() self._alert.props.title = \ _('Do you really want to load new images?') self._alert.props.msg = _('You have done work on this story.' ' Do you want to overwrite it?') self._alert.connect('response', self._confirmation_alert_cb) self.add_alert(self._alert) else: self.autoplay_button.set_sensitive(False) self._game.new_game() def _confirmation_alert_cb(self, alert, response_id): self.remove_alert(alert) if response_id is Gtk.ResponseType.OK: self.autoplay_button.set_sensitive(False) self._clear_text() self._clear_audio_notes() self._game.new_game() def write_file(self, file_path): ''' Write the grid status to the Journal ''' dot_list = self._game.save_game() self.metadata['dotlist'] = '' for dot in dot_list: self.metadata['dotlist'] += str(dot) if dot_list.index(dot) < len(dot_list) - 1: self.metadata['dotlist'] += ' ' self.metadata['mode'] = self._game.get_mode() self.speak_text_cb() def _restore(self): ''' Restore the game state from metadata ''' dot_list = [] dots = self.metadata['dotlist'].split() for dot in dots: dot_list.append(int(dot)) self._game.restore_game(dot_list) def _search_for_audio_note(self, obj_id, target=None): ''' Look to see if there is already a sound recorded for this dsobject: the object id is stored in a tag in the audio file. ''' dsobjects, nobjects = datastore.find({'mime_type': ['audio/ogg']}) # Look for tag that matches the target object id if target is None: if self._game.get_mode() == 'array': target = obj_id else: target = '%s-%d' % (obj_id, self._game.current_image) for dsobject in dsobjects: if 'tags' in dsobject.metadata and \ target in dsobject.metadata['tags']: _logger.debug('Found audio note') self.metadata['dirty'] = 'True' return dsobject return None def _do_save_as_pdf_cb(self, button=None): self._waiting_cursor() self._notify_successful_save(title=_('Save as PDF')) GObject.idle_add(self._save_as_pdf) def _save_as_pdf(self): self.speak_text_cb() file_path = os.path.join(self.datapath, 'output.pdf') if 'description' in self.metadata: save_pdf(self, file_path, self._nick, description=self.metadata['description']) else: save_pdf(self, file_path, self._nick) dsobject = datastore.create() dsobject.metadata['title'] = '%s %s' % \ (self.metadata['title'], _('PDF')) dsobject.metadata['icon-color'] = profile.get_color().to_string() dsobject.metadata['mime_type'] = 'application/pdf' dsobject.metadata['activity'] = 'org.laptop.sugar3.ReadActivity' dsobject.set_file_path(file_path) datastore.write(dsobject) dsobject.destroy() os.remove(file_path) GObject.timeout_add(1000, self._remove_alert) def _do_save_as_image_cb(self, button=None): self._waiting_cursor() self._notify_successful_save(title=_('Save as image')) GObject.idle_add(self._save_as_image) def _save_as_image(self): ''' Grab the current canvas and save it to the Journal. ''' if self._uid is None: self._uid = generate_uid() if self._game.get_mode() == 'array': target = self._uid else: target = '%s-%d' % (self._uid, self._game.current_image) file_path = os.path.join(self.datapath, 'story.png') png_surface = self._game.export() png_surface.write_to_png(file_path) dsobject = datastore.create() dsobject.metadata['title'] = '%s %s' % \ (self.metadata['title'], _('image')) dsobject.metadata['icon-color'] = profile.get_color().to_string() dsobject.metadata['mime_type'] = 'image/png' dsobject.metadata['tags'] = target dsobject.set_file_path(file_path) datastore.write(dsobject) dsobject.destroy() os.remove(file_path) GObject.timeout_add(1000, self._remove_alert) def record_cb(self, button=None, cb=None): ''' Start/stop audio recording ''' if self._arecord is None: self._arecord = Arecord(self) if self.recording: # Was recording, so stop and later save self._game.set_record_icon_state(False) self._arecord.stop_recording_audio() self.recording = False self.busy() GObject.timeout_add(100, self._is_record_complete_timeout, cb) else: # Wasn't recording, so start self._game.set_record_icon_state(True) self._arecord.record_audio() self.recording = True def _is_record_complete_timeout(self, cb=None): if not self._arecord.is_complete(): return True # call back later self._save_recording() self.unbusy() if cb is not None: cb() return False # do not call back def playback_recording_cb(self, button=None): ''' Play back current recording ''' if self.recording: # Stop recording if we happen to be recording self.record_cb(cb=self._playback_recording) else: self._playback_recording() def _playback_recording(self): path = os.path.join(self.datapath, 'output.ogg') if self._uid is not None: dsobject = self._search_for_audio_note(self._uid) if dsobject is not None: path = dsobject.file_path aplay.play(path) def _save_recording(self): self.metadata['dirty'] = 'True' # So we know that we've done work if os.path.exists(os.path.join(self.datapath, 'output.ogg')): _logger.debug('Saving recording to Journal...') if self._uid is None: self._uid = generate_uid() if self._game.get_mode() == 'array': target = self._uid else: target = '%s-%d' % (self._uid, self._game.current_image) dsobject = self._search_for_audio_note(target) if dsobject is None: dsobject = datastore.create() dsobject.metadata['title'] = \ _('audio note for %s') % (self.metadata['title']) dsobject.metadata['icon-color'] = profile.get_color().to_string() dsobject.metadata['mime_type'] = 'audio/ogg' if self._uid is not None: dsobject.metadata['tags'] = target dsobject.set_file_path(os.path.join(self.datapath, 'output.ogg')) datastore.write(dsobject) dsobject.destroy() # Enable playback after record is finished self._game.set_play_icon_state(True) self.metadata['dirty'] = 'True' # Always save an image with the recording. # self._do_save_as_image_cb() else: _logger.debug('Nothing to save...') return def _notify_successful_save(self, title='', msg=''): ''' Notify user when saves are completed ''' self._alert = Alert() self._alert.props.title = title self._alert.props.msg = msg self.add_alert(self._alert) self._alert.show() def _remove_alert(self): if self._alert is not None: self.remove_alert(self._alert) self._alert = None self._restore_cursor() # Collaboration-related methods def _setup_presence_service(self): ''' Setup the Presence Service. ''' self.pservice = presenceservice.get_instance() self.initiating = None # sharing (True) or joining (False) owner = self.pservice.get_owner() self.owner = owner self._share = '' self.connect('shared', self._shared_cb) self.connect('joined', self._joined_cb) def _shared_cb(self, activity): ''' Either set up initial share...''' self._new_tube_common(True) def _joined_cb(self, activity): ''' ...or join an exisiting share. ''' self._new_tube_common(False) def _new_tube_common(self, sharer): ''' Joining and sharing are mostly the same... ''' shared_activity = self.get_shared_activity() if shared_activity is None: _logger.error('Failed to share or join activity') return self.initiating = sharer self.waiting_for_hand = not sharer self.conn = shared_activity.telepathy_conn self.tubes_chan = shared_activity.telepathy_tubes_chan self.text_chan = shared_activity.telepathy_text_chan self.tubes_chan[ TelepathyGLib.IFACE_CHANNEL_TYPE_TUBES].connect_to_signal( 'NewTube', self._new_tube_cb) if sharer: _logger.debug('This is my activity: making a tube...') self.tubes_chan[ TelepathyGLib.IFACE_CHANNEL_TYPE_TUBES].OfferDBusTube( SERVICE, {}) else: _logger.debug('I am joining an activity: waiting for a tube...') self.tubes_chan[TelepathyGLib.IFACE_CHANNEL_TYPE_TUBES].ListTubes( reply_handler=self._list_tubes_reply_cb, error_handler=self._list_tubes_error_cb) self._game.set_sharing(True) def _list_tubes_reply_cb(self, tubes): ''' Reply to a list request. ''' for tube_info in tubes: self._new_tube_cb(*tube_info) def _list_tubes_error_cb(self, e): ''' Log errors. ''' _logger.error('Error: ListTubes() failed: %s' % (e)) def _new_tube_cb(self, id, initiator, type, service, params, state): ''' Create a new tube. ''' _logger.debug('New tube: ID=%d initator=%d type=%d service=%s' ' params=%r state=%d' % (id, initiator, type, service, params, state)) if (type == TelepathyGLib.TubeType.DBUS and service == SERVICE): if state == TelepathyGLib.TubeState.LOCAL_PENDING: self.tubes_chan[ TelepathyGLib.IFACE_CHANNEL_TYPE_TUBES].AcceptDBusTube(id) self.collab = CollabWrapper(self) self.collab.message.connect(self.event_received_cb) self.collab.setup() def _setup_dispatch_table(self): ''' Associate tokens with commands. ''' self._processing_methods = { 'n': [self._receive_new_images, 'get a new game grid'], 'p': [self._receive_dot_click, 'get a dot click'], } def event_received_cb(self, collab, buddy, msg): ''' Data from a tube has arrived. ''' command = msg.get("command") if command is None: return payload = msg.get("payload") self._processing_methods[command][0](payload) def send_new_images(self): ''' Send a new image grid to all players ''' self.send_event("n", json_dump(self._game.save_game())) def _receive_new_images(self, payload): ''' Sharer can start a new game. ''' dot_list = json_load(payload) self._game.restore_game(dot_list) def send_dot_click(self, dot, color): ''' Send a dot click to all the players ''' self.send_event("p", json_dump([dot, color])) def _receive_dot_click(self, payload): ''' When a dot is clicked, everyone should change its color. ''' (dot, color) = json_load(payload) self._game.remote_button_press(dot, color) def send_event(self, command, payload): ''' Send event through the tube. ''' if hasattr(self, 'chattube') and self.collab is not None: self.collab.SendText(dict( command=command, payload=payload, ))
class DevTutorActivity(activity.Activity): """DevTutorActivity class as specified in activity.info""" def __init__(self, handle): activity.Activity.__init__(self, handle) self.max_participants = 10 # toolbar with the new toolbar redesign toolbar_box = ToolbarBox() activity_button = ActivityToolbarButton(self) toolbar_box.toolbar.insert(activity_button, 0) activity_button.show() self.back_button = BackButton() self.back_button.connect('clicked', self.show_options1) toolbar_box.toolbar.insert(self.back_button, -1) self.back_button.show() separator = Gtk.SeparatorToolItem() separator.props.draw = False separator.set_expand(True) toolbar_box.toolbar.insert(separator, -1) separator.show() stop_button = StopButton(self) toolbar_box.toolbar.insert(stop_button, -1) stop_button.show() self.set_toolbar_box(toolbar_box) toolbar_box.show() self.show_options() self._logger = logging.getLogger('hellomesh-activity') self.hellotube = None # Shared session self.initiating = False # get the Presence Service self.pservice = presenceservice.get_instance() # Buddy object for you owner = self.pservice.get_owner() self.owner = owner self.connect('shared', self._shared_cb) self.connect('joined', self._joined_cb) def show_options1(self, data=None): self.show_options() def show_options(self): self.main_container = Gtk.VBox() self.add_padding() self.line1 = Gtk.HBox() button1 = Gtk.Button("Show modules") #button1.set_size_request(200,80) #self.line1.pack_start(button1, False, False, 0) self.line1.add(button1) button1.connect('clicked', self.show_modules, None) button1.get_child().modify_font(Pango.FontDescription("Sans 14")) button1.show() self.main_container.add(self.line1) self.line1.show() self.add_padding() self.line2 = Gtk.HBox() button2 = Gtk.Button("Show activities") #button2.set_size_request(200,80) #self.line2.pack_start(button2, False, False, 0) self.line2.add(button2) button2.connect('clicked', self.show_activity_list, None) button2.get_child().modify_font(Pango.FontDescription("Sans 14")) button2.show() self.main_container.add(self.line2) self.line2.show() self.add_padding() self.line3 = Gtk.HBox() text = Gtk.TextView() self.entry = Gtk.Entry() self.entry.set_sensitive(True) self.entry.connect('activate', self.entry_activate_cb) self.entry.show() self.line3.add(self.entry) self.main_container.add(self.line3) self.line3.show() self.set_canvas(self.main_container) self.main_container.show() def add_padding(self): self.line_space1 = Gtk.HBox() self.main_container.add(self.line_space1) self.line_space1.show() self.line_space2 = Gtk.HBox() self.main_container.add(self.line_space2) self.line_space2.show() def show_modules(self, sender, data=None): self.mod = ShowModules(self.set_canvas) self.mod.show_modules() def show_activity_list(self, sender, data=None): self.back_button.connect('clicked', self.show_options1) self.main_container = Gtk.VBox() self.add_padding() self.line1 = Gtk.HBox() button1 = Gtk.Button("Hello World activity") #button1.set_size_request(200,80) #self.line1.pack_start(button1, False, False, 0) self.line1.add(button1) button1.connect('clicked', self.show_labels_hello, None) button1.get_child().modify_font(Pango.FontDescription("Sans 14")) button1.show() self.main_container.add(self.line1) self.line1.show() self.add_padding() self.line2 = Gtk.HBox() button2 = Gtk.Button("Write activity") #button2.set_size_request(200,80) #self.line2.pack_start(button2, False, False, 0) self.line2.add(button2) button2.connect('clicked', self.show_labels_write, None) button2.get_child().modify_font(Pango.FontDescription("Sans 14")) button2.show() self.main_container.add(self.line2) self.line2.show() self.add_padding() self.set_canvas(self.main_container) self.main_container.show() def show_labels_hello(self, sender, data=None): self.back_button.connect('clicked', self.show_activity_list) self.main_container = Gtk.VBox() self.add_padding() self.line1 = Gtk.HBox() self.label1 = Gtk.Label( _("Hello World activity step 1 - call activity.__init__")) self.label1.set_line_wrap(True) self.label1.modify_font(Pango.FontDescription("Sans 12")) self.line1.add(self.label1) self.label1.show() button1 = Gtk.Button("Show result") button1.set_size_request(200, 80) self.line1.pack_start(button1, False, False, 0) button1.connect('clicked', self.hello_launch1, None) button1.get_child().modify_font(Pango.FontDescription("Sans 14")) button1.show() self.main_container.add(self.line1) self.line1.show() self.add_padding() self.line2 = Gtk.HBox() self.label2 = Gtk.Label(_("Hello Word activity step 2 - add toolbox")) self.label2.set_line_wrap(True) self.label2.modify_font(Pango.FontDescription("Sans 12")) self.line2.add(self.label2) self.label2.show() button2 = Gtk.Button("Show result") button2.set_size_request(200, 80) self.line2.pack_start(button2, False, False, 0) button2.connect('clicked', self.hello_launch2, None) button2.get_child().modify_font(Pango.FontDescription("Sans 14")) button2.show() self.main_container.add(self.line2) self.line2.show() self.add_padding() self.line3 = Gtk.HBox() self.label3 = Gtk.Label( _("Hello World activity step 3 - add hello world label")) self.label3.set_line_wrap(True) self.label3.modify_font(Pango.FontDescription("Sans 12")) self.line3.add(self.label3) self.label3.show() button3 = Gtk.Button("Show result") button3.set_size_request(200, 80) self.line3.pack_start(button3, False, False, 0) button3.connect('clicked', self.hello_launch3, None) button3.get_child().modify_font(Pango.FontDescription("Sans 14")) button3.show() self.main_container.add(self.line3) self.line3.show() self.add_padding() self.line4 = Gtk.HBox() self.label4 = Gtk.Label( _("Hello World activity step 4 - add rotate button")) self.label4.set_line_wrap(True) self.label4.modify_font(Pango.FontDescription("Sans 12")) self.line4.add(self.label4) self.label4.show() button4 = Gtk.Button("Show result") button4.set_size_request(200, 80) self.line4.pack_start(button4, False, False, 0) button4.connect('clicked', self.hello_launch4, None) button4.get_child().modify_font(Pango.FontDescription("Sans 14")) button4.show() self.main_container.add(self.line4) self.line4.show() self.add_padding() self.set_canvas(self.main_container) self.main_container.show() def show_labels_write(self, sender, data=None): self.back_button.connect('clicked', self.show_activity_list) self.main_container = Gtk.VBox() self.add_padding() self.line1 = Gtk.HBox() self.label1 = Gtk.Label(_("Write activity step 1 ")) self.label1.set_line_wrap(True) self.label1.modify_font(Pango.FontDescription("Sans 12")) self.line1.add(self.label1) self.label1.show() button1 = Gtk.Button("Show result") button1.set_size_request(200, 80) self.line1.pack_start(button1, False, False, 0) button1.connect('clicked', self.write_launch1, None) button1.get_child().modify_font(Pango.FontDescription("Sans 14")) button1.show() self.main_container.add(self.line1) self.line1.show() self.add_padding() self.line2 = Gtk.HBox() self.label2 = Gtk.Label(_("Write activity step 2")) self.label2.set_line_wrap(True) self.label2.modify_font(Pango.FontDescription("Sans 12")) self.line2.add(self.label2) self.label2.show() button2 = Gtk.Button("Show result") button2.set_size_request(200, 80) self.line2.pack_start(button2, False, False, 0) button2.connect('clicked', self.write_launch2, None) button2.get_child().modify_font(Pango.FontDescription("Sans 14")) button2.show() self.main_container.add(self.line2) self.line2.show() self.set_canvas(self.main_container) self.add_padding() self.line3 = Gtk.HBox() self.label3 = Gtk.Label(_("Write activity step 3")) self.label3.set_line_wrap(True) self.label3.modify_font(Pango.FontDescription("Sans 12")) self.line3.add(self.label3) self.label3.show() button3 = Gtk.Button("Show result") button3.set_size_request(200, 80) self.line3.pack_start(button3, False, False, 0) button3.connect('clicked', self.write_launch3, None) button3.get_child().modify_font(Pango.FontDescription("Sans 14")) button3.show() self.main_container.add(self.line3) self.line3.show() self.add_padding() self.set_canvas(self.main_container) self.main_container.show() def hello_launch1(self, sender, data=None): f = open('/tmp/1', 'w') os.putenv('TUTOR_CLASS', 'HelloWorldActivity') self.launch() def hello_launch2(self, sender, data=None): f = open('/tmp/2', 'w') self.hello_launch1(sender, data) def hello_launch3(self, sender, data=None): f = open('/tmp/3', 'w') self.hello_launch2(sender, data) def hello_launch4(self, sender, data=None): f = open('/tmp/4', 'w') self.hello_launch3(sender, data) def write_launch1(self, sender, data=None): f = open('/tmp/1', 'w') os.putenv('TUTOR_CLASS', 'AbiWordActivity') self.launch() def write_launch2(self, sender, data=None): f = open('/tmp/2', 'w') self.write_launch1(sender, data) def write_launch3(self, sender, data=None): f = open('/tmp/3', 'w') self.write_launch2(sender, data) def launch(self): subprocess.Popen(['sugar-launch', 'org.sugarlabs.DevTutor']) def entry_activate_cb(self, entry): """Handle the event when Enter is pressed in the Entry.""" text = entry.props.text if self.hellotube is not None: self.hellotube.SendText(text) def entry_text_update_cb(self, text): """Update Entry text when text received from others.""" self.entry.props.text = text def _alert(self, title, text=None): try: self.remove_alert(self.alert) finally: self.alert = Alert() self.alert.props.title = title self.alert.props.msg = text self.add_alert(self.alert) self.alert.connect('response', self._alert_cancel_cb) self.alert.show() def _alert_cancel_cb(self, alert, response_id): #self.remove_alert(alert) pass def _shared_cb(self, activity): self._logger.debug('My activity was shared') self.alert = Alert() self.alert.props.title = 'Shared Activity' self.alert.props.msg = 'Shared messages to be displayed here' self.add_alert(self.alert) self.initiating = True self._sharing_setup() self._logger.debug('This is my activity: making a tube...') id = self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube( SERVICE, {}) def _sharing_setup(self): if self.shared_activity is None: self._logger.error('Failed to share or join activity') return self.conn = self.shared_activity.telepathy_conn self.tubes_chan = self.shared_activity.telepathy_tubes_chan self.text_chan = self.shared_activity.telepathy_text_chan self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal( 'NewTube', self._new_tube_cb) self.shared_activity.connect('buddy-joined', self._buddy_joined_cb) self.shared_activity.connect('buddy-left', self._buddy_left_cb) self.entry.set_sensitive(True) self.entry.grab_focus() # Optional - included for example: # Find out who's already in the shared activity: for buddy in self.shared_activity.get_joined_buddies(): self._logger.debug('Buddy %s is already in the activity', buddy.props.nick) def _list_tubes_reply_cb(self, tubes): for tube_info in tubes: self._new_tube_cb(*tube_info) def _list_tubes_error_cb(self, e): self._logger.error('ListTubes() failed: %s', e) def _joined_cb(self, activity): if not self.shared_activity: return self._logger.debug('Joined an existing shared activity') self._alert('Joined', 'Joined a shared activity') self.initiating = False self._sharing_setup() self._logger.debug('This is not my activity: waiting for a tube...') self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes( reply_handler=self._list_tubes_reply_cb, error_handler=self._list_tubes_error_cb) def _new_tube_cb(self, id, initiator, type, service, params, state): self._logger.debug( 'New tube: ID=%d initator=%d type=%d service=%s ' 'params=%r state=%d', id, initiator, type, service, params, state) if (type == telepathy.TUBE_TYPE_DBUS and service == SERVICE): if state == telepathy.TUBE_STATE_LOCAL_PENDING: self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].AcceptDBusTube( id) tube_conn = TubeConnection( self.conn, self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES], id, group_iface=self.text_chan[telepathy.CHANNEL_INTERFACE_GROUP]) self.hellotube = TextSync(tube_conn, self.initiating, self.entry_text_update_cb, self._alert, self._get_buddy) def _buddy_joined_cb(self, activity, buddy): """Called when a buddy joins the shared activity. This doesn't do much here as HelloMesh doesn't have much functionality. It's up to you do do interesting things with the Buddy... """ self._logger.debug('Buddy %s joined', buddy.props.nick) self._alert('Buddy joined', '%s joined' % buddy.props.nick) def _buddy_left_cb(self, activity, buddy): """Called when a buddy leaves the shared activity. This doesn't do much here as HelloMesh doesn't have much functionality. It's up to you do do interesting things with the Buddy... """ self._logger.debug('Buddy %s left', buddy.props.nick) self._alert('Buddy left', '%s left' % buddy.props.nick) def _get_buddy(self, cs_handle): """Get a Buddy from a channel specific handle.""" self._logger.debug('Trying to find owner of handle %u...', cs_handle) group = self.text_chan[telepathy.CHANNEL_INTERFACE_GROUP] my_csh = group.GetSelfHandle() self._logger.debug('My handle in that group is %u', my_csh) if my_csh == cs_handle: handle = self.conn.GetSelfHandle() self._logger.debug('CS handle %u belongs to me, %u', cs_handle, handle) elif group.GetGroupFlags( ) & telepathy.CHANNEL_GROUP_FLAG_CHANNEL_SPECIFIC_HANDLES: handle = group.GetHandleOwners([cs_handle])[0] self._logger.debug('CS handle %u belongs to %u', cs_handle, handle) else: handle = cs_handle self._logger.debug('non-CS handle %u belongs to itself', handle) # XXX: deal with failure to get the handle owner assert handle != 0 return self.pservice.get_buddy_by_telepathy_handle( self.conn.service_name, self.conn.object_path, handle)
class Download(object): def __init__(self, webkit_download, activity): self._download = webkit_download self._activity = activity self._source = self._download.get_request().get_uri() logging.debug('START Download %s', self._source) self.datastore_deleted_handler = None self.dl_jobject = None self._object_id = None self._start_alert = None self._stop_alert = None self._dest_path = '' self._progress = 0 self._last_update_progress = 0 self._progress_sid = None self.temp_path = os.path.join(self._activity.get_activity_root(), 'instance') if not os.path.exists(self.temp_path): os.makedirs(self.temp_path) self._download.connect('failed', self.__download_failed_cb) self._download.connect('finished', self.__download_finished_cb) self._download.connect('received-data', self.__download_received_data_cb) # Notify response is called before decide destination self._download.connect('notify::response', self.__notify_response_cb) self._download.connect('decide-destination', self.__decide_destination_cb) self._download.connect('created-destination', self.__created_destination_cb) def __notify_response_cb(self, download, pspec): logging.debug('__notify_response_cb') response = download.get_response() # Check free space and cancel the download if there is not enough. total_size = response.get_content_length() logging.debug('Total size of the file: %s', total_size) enough_space = self.enough_space(total_size, path=self.temp_path) if not enough_space: logging.debug('Download canceled because of Disk Space') self.cancel() self._canceled_alert = Alert() self._canceled_alert.props.title = _('Not enough space ' 'to download') total_size_mb = total_size / 1024.0**2 free_space_mb = (self._free_available_space( path=self.temp_path) - SPACE_THRESHOLD) \ / 1024.0 ** 2 filename = response.get_suggested_filename() self._canceled_alert.props.msg = \ _('Download "%{filename}" requires %{total_size_in_mb}' ' MB of free space, only %{free_space_in_mb} MB' ' is available' % {'filename': filename, 'total_size_in_mb': format_float(total_size_mb), 'free_space_in_mb': format_float(free_space_mb)}) ok_icon = Icon(icon_name='dialog-ok') self._canceled_alert.add_button(Gtk.ResponseType.OK, _('Ok'), ok_icon) ok_icon.show() self._canceled_alert.connect('response', self.__canceled_response_cb) self._activity.add_alert(self._canceled_alert) def __canceled_response_cb(self, alert, response_id): self._activity.remove_alert(alert) def __decide_destination_cb(self, download, suggested_filename): logging.debug('__decide_desintation_cb suggests %s', suggested_filename) self._start_alert = TimeoutAlert(9) self._start_alert.props.title = _('Downloading') self._start_alert.props.msg = suggested_filename self._activity.add_alert(self._start_alert) self._start_alert.connect('response', self.__start_response_cb) self._start_alert.show() self._suggested_filename = suggested_filename # figure out download URI self._dest_path = tempfile.mktemp(dir=self.temp_path, suffix=suggested_filename, prefix='tmp') logging.debug('Download destination path: %s' % self._dest_path) self._download.set_destination('file://' + self._dest_path) logging.error(self._download.get_destination) return True def __created_destination_cb(self, download, dest): logging.debug('__created_destination_cb at %s', dest) self._create_journal_object() self._object_id = self.dl_jobject.object_id def _update_progress(self): if self._progress > self._last_update_progress: self._last_update_progress = self._progress self.dl_jobject.metadata['progress'] = str(self._progress) datastore.write(self.dl_jobject) self._progress_sid = None return False def __download_received_data_cb(self, download, data_size): self._progress = int(self._download.get_estimated_progress() * 100) if self._progress_sid is None: self._progress_sid = GObject.timeout_add(PROGRESS_TIMEOUT, self._update_progress) def __download_finished_cb(self, download): if hasattr(self._activity, 'busy'): self._activity.unbusy() if self._progress_sid is not None: GObject.source_remove(self._progress_sid) if self.dl_jobject is None: return # the "failed" signal was observed self.dl_jobject.metadata['title'] = self._suggested_filename self.dl_jobject.metadata['description'] = _('From: %s') \ % self._source self.dl_jobject.metadata['progress'] = '100' self.dl_jobject.file_path = self._dest_path mime_type = Gio.content_type_guess(self._dest_path)[0] if mime_type != 'application/vnd.olpc-sugar': mime_type = download.get_response().get_mime_type() self.dl_jobject.metadata['mime_type'] = mime_type if mime_type in ('image/bmp', 'image/gif', 'image/jpeg', 'image/png', 'image/tiff'): preview = self._get_preview() if preview is not None: self.dl_jobject.metadata['preview'] = \ dbus.ByteArray(preview) datastore.write(self.dl_jobject, transfer_ownership=True, reply_handler=self.__internal_save_cb, error_handler=self.__internal_error_cb, timeout=360) if self._start_alert is not None: self._activity.remove_alert(self._start_alert) self._stop_alert = Alert() self._stop_alert.props.title = _('Downloaded') self._stop_alert.props.msg = self._suggested_filename bundle = None if _HAS_BUNDLE_LAUNCHER: bundle = get_bundle(object_id=self._object_id) if bundle is not None: icon = Icon(file=bundle.get_icon()) label = _('Open with %s') % bundle.get_name() response_id = Gtk.ResponseType.APPLY else: icon = Icon(icon_name='zoom-activity') label = _('Show in Journal') response_id = Gtk.ResponseType.ACCEPT self._stop_alert.add_button(response_id, label, icon) icon.show() ok_icon = Icon(icon_name='dialog-ok') self._stop_alert.add_button(Gtk.ResponseType.OK, _('Ok'), ok_icon) ok_icon.show() self._activity.add_alert(self._stop_alert) self._stop_alert.connect('response', self.__stop_response_cb) self._stop_alert.show() def __download_failed_cb(self, download, error): logging.error('Error downloading URI due to %s' % error) self.cleanup() def __internal_save_cb(self): logging.debug('Object saved succesfully to the datastore.') self.cleanup() def __internal_error_cb(self, err): logging.debug('Error saving activity object to datastore: %s' % err) self.cleanup() def __start_response_cb(self, alert, response_id): if response_id is Gtk.ResponseType.CANCEL: logging.debug('Download Canceled') self.cancel() try: datastore.delete(self._object_id) except Exception, e: logging.warning('Object has been deleted already %s' % e) self.cleanup() if self._stop_alert is not None: self._activity.remove_alert(self._stop_alert) self._activity.remove_alert(alert) self._start_alert = None
class Download(object): def __init__(self, webkit_download, activity): self._download = webkit_download self._activity = activity self._source = self._download.get_request().get_uri() logging.debug('START Download %s', self._source) self.datastore_deleted_handler = None self.dl_jobject = None self._object_id = None self._start_alert = None self._stop_alert = None self._dest_path = '' self._progress = 0 self._last_update_progress = 0 self._progress_sid = None self.temp_path = os.path.join( self._activity.get_activity_root(), 'instance') if not os.path.exists(self.temp_path): os.makedirs(self.temp_path) self._download.connect('failed', self.__download_failed_cb) self._download.connect('finished', self.__download_finished_cb) self._download.connect('received-data', self.__download_received_data_cb) # Notify response is called before decide destination self._download.connect('notify::response', self.__notify_response_cb) self._download.connect('decide-destination', self.__decide_destination_cb) self._download.connect('created-destination', self.__created_destination_cb) def __notify_response_cb(self, download, pspec): logging.debug('__notify_response_cb') response = download.get_response() # Check free space and cancel the download if there is not enough. total_size = response.get_content_length() logging.debug('Total size of the file: %s', total_size) enough_space = self.enough_space( total_size, path=self.temp_path) if not enough_space: logging.debug('Download canceled because of Disk Space') self.cancel() self._canceled_alert = Alert() self._canceled_alert.props.title = _('Not enough space ' 'to download') total_size_mb = total_size / 1024.0 ** 2 free_space_mb = (self._free_available_space( path=self.temp_path) - SPACE_THRESHOLD) \ / 1024.0 ** 2 filename = response.get_suggested_filename() self._canceled_alert.props.msg = \ _('Download "%{filename}" requires %{total_size_in_mb}' ' MB of free space, only %{free_space_in_mb} MB' ' is available' % {'filename': filename, 'total_size_in_mb': format_float(total_size_mb), 'free_space_in_mb': format_float(free_space_mb)}) ok_icon = Icon(icon_name='dialog-ok') self._canceled_alert.add_button(Gtk.ResponseType.OK, _('Ok'), ok_icon) ok_icon.show() self._canceled_alert.connect('response', self.__canceled_response_cb) self._activity.add_alert(self._canceled_alert) def __canceled_response_cb(self, alert, response_id): self._activity.remove_alert(alert) def __decide_destination_cb(self, download, suggested_filename): logging.debug('__decide_desintation_cb suggests %s', suggested_filename) self._start_alert = TimeoutAlert(9) self._start_alert.props.title = _('Downloading') self._start_alert.props.msg = suggested_filename self._activity.add_alert(self._start_alert) self._start_alert.connect('response', self.__start_response_cb) self._start_alert.show() self._suggested_filename = suggested_filename # figure out download URI self._dest_path = tempfile.mktemp( dir=self.temp_path, suffix=suggested_filename, prefix='tmp') logging.debug('Download destination path: %s' % self._dest_path) self._download.set_destination('file://' + self._dest_path) logging.error(self._download.get_destination) return True def __created_destination_cb(self, download, dest): logging.debug('__created_destination_cb at %s', dest) self._create_journal_object() self._object_id = self.dl_jobject.object_id def _update_progress(self): if self._progress > self._last_update_progress: self._last_update_progress = self._progress self.dl_jobject.metadata['progress'] = str(self._progress) datastore.write(self.dl_jobject) self._progress_sid = None return False def __download_received_data_cb(self, download, data_size): self._progress = int(self._download.get_estimated_progress() * 100) if self._progress_sid is None: self._progress_sid = GObject.timeout_add( PROGRESS_TIMEOUT, self._update_progress) def __download_finished_cb(self, download): if hasattr(self._activity, 'busy'): self._activity.unbusy() if self._progress_sid is not None: GObject.source_remove(self._progress_sid) if self.dl_jobject is None: return # the "failed" signal was observed self.dl_jobject.metadata['title'] = self._suggested_filename self.dl_jobject.metadata['description'] = _('From: %s') \ % self._source self.dl_jobject.metadata['progress'] = '100' self.dl_jobject.file_path = self._dest_path mime_type = Gio.content_type_guess(self._dest_path)[0] if mime_type != 'application/vnd.olpc-sugar': mime_type = download.get_response().get_mime_type() self.dl_jobject.metadata['mime_type'] = mime_type if mime_type in ('image/bmp', 'image/gif', 'image/jpeg', 'image/png', 'image/tiff'): preview = self._get_preview() if preview is not None: self.dl_jobject.metadata['preview'] = \ dbus.ByteArray(preview) datastore.write(self.dl_jobject, transfer_ownership=True, reply_handler=self.__internal_save_cb, error_handler=self.__internal_error_cb, timeout=360) if self._start_alert is not None: self._activity.remove_alert(self._start_alert) self._stop_alert = Alert() self._stop_alert.props.title = _('Downloaded') self._stop_alert.props.msg = self._suggested_filename bundle = None if _HAS_BUNDLE_LAUNCHER: bundle = get_bundle(object_id=self._object_id) if bundle is not None: icon = Icon(file=bundle.get_icon()) label = _('Open with %s') % bundle.get_name() response_id = Gtk.ResponseType.APPLY else: icon = Icon(icon_name='zoom-activity') label = _('Show in Journal') response_id = Gtk.ResponseType.ACCEPT self._stop_alert.add_button(response_id, label, icon) icon.show() ok_icon = Icon(icon_name='dialog-ok') self._stop_alert.add_button(Gtk.ResponseType.OK, _('Ok'), ok_icon) ok_icon.show() self._activity.add_alert(self._stop_alert) self._stop_alert.connect('response', self.__stop_response_cb) self._stop_alert.show() def __download_failed_cb(self, download, error): logging.error('Error downloading URI due to %s' % error) self.cleanup() def __internal_save_cb(self): logging.debug('Object saved succesfully to the datastore.') self.cleanup() def __internal_error_cb(self, err): logging.debug('Error saving activity object to datastore: %s' % err) self.cleanup() def __start_response_cb(self, alert, response_id): if response_id is Gtk.ResponseType.CANCEL: logging.debug('Download Canceled') self.cancel() try: datastore.delete(self._object_id) except Exception, e: logging.warning('Object has been deleted already %s' % e) self.cleanup() if self._stop_alert is not None: self._activity.remove_alert(self._stop_alert) self._activity.remove_alert(alert) self._start_alert = None
class BatchOperator(GObject.GObject): """ This class implements the course of actions that happens when clicking upon an BatchOperation (eg. Batch-Copy-Toolbar-button; Batch-Copy-To-Journal-button; Batch-Copy-To-Documents-button; Batch-Copy-To-Mounted-Drive-button; Batch-Copy-To-Clipboard-button; Batch-Erase-Button; """ def __init__(self, journalactivity, uid_list, alert_title, alert_message, operation_cb): GObject.GObject.__init__(self) self._journalactivity = journalactivity self._uid_list = uid_list[:] self._alert_title = alert_title self._alert_message = alert_message self._operation_cb = operation_cb self._show_confirmation_alert() def _show_confirmation_alert(self): self._journalactivity.freeze_ui() GObject.idle_add(self.__show_confirmation_alert_internal) def __show_confirmation_alert_internal(self): # Show a alert requesting confirmation before run the batch operation self._confirmation_alert = Alert() self._confirmation_alert.props.title = self._alert_title self._confirmation_alert.props.msg = self._alert_message stop_icon = Icon(icon_name='dialog-cancel') self._confirmation_alert.add_button(Gtk.ResponseType.CANCEL, _('Stop'), stop_icon) stop_icon.show() ok_icon = Icon(icon_name='dialog-ok') self._confirmation_alert.add_button(Gtk.ResponseType.OK, _('Continue'), ok_icon) ok_icon.show() self._journalactivity.add_alert(self._confirmation_alert) self._confirmation_alert.connect('response', self.__confirmation_response_cb) self._confirmation_alert.show() def __confirmation_response_cb(self, alert, response): if response == Gtk.ResponseType.CANCEL: self._journalactivity.unfreeze_ui() self._journalactivity.remove_alert(alert) # this is only in the case the operation already started # and the user want stop it. self._stop_batch_operation() else: GObject.idle_add(self._prepare_batch_execution) def _prepare_batch_execution(self): self._object_index = 0 # Next, proceed with the objects self._operate_by_uid() def _operate_by_uid(self): GObject.idle_add(self._operate_by_uid_internal) def _operate_by_uid_internal(self): # If there is still some uid left, proceed with the operation. # Else, proceed to post-operations. if self._object_index < len(self._uid_list): uid = self._uid_list[self._object_index] metadata = model.get(uid) title = None if 'title' in metadata: title = metadata['title'] if title is None or title == '': title = _('Untitled') alert_message = _('%(index)d of %(total)d : %(object_title)s') % { 'index': self._object_index, 'total': len(self._uid_list), 'object_title': title } self._confirmation_alert.props.msg = alert_message GObject.idle_add(self._operate_per_metadata, metadata) else: self._finish_batch_execution() def _operate_per_metadata(self, metadata): self._operation_cb(metadata) # process the next self._object_index = self._object_index + 1 GObject.idle_add(self._operate_by_uid_internal) def _stop_batch_execution(self): self._object_index = len(self._uid_list) def _finish_batch_execution(self): self._journalactivity.unfreeze_ui() self._journalactivity.remove_alert(self._confirmation_alert) self._journalactivity.update_selected_items_ui()
class JukeboxActivity(activity.Activity): __gsignals__ = { 'playlist-finished': (GObject.SignalFlags.RUN_FIRST, None, []), } def __init__(self, handle): activity.Activity.__init__(self, handle) self.player = None self._alert = None self._playlist_jobject = None self.set_title(_('Jukebox Activity')) self.max_participants = 1 self._toolbar_box = ToolbarBox() activity_button = ActivityToolbarButton(self) activity_toolbar = activity_button.page self._toolbar_box.toolbar.insert(activity_button, 0) self.title_entry = activity_toolbar.title self._view_toolbar = ViewToolbar() self._view_toolbar.connect('go-fullscreen', self.__go_fullscreen_cb) self._view_toolbar.connect('toggle-playlist', self.__toggle_playlist_cb) view_toolbar_button = ToolbarButton( page=self._view_toolbar, icon_name='toolbar-view') self._view_toolbar.show() self._toolbar_box.toolbar.insert(view_toolbar_button, -1) view_toolbar_button.show() self._control_toolbar = Gtk.Toolbar() self._control_toolbar_button = ToolbarButton( page=self._control_toolbar, icon_name='media-playback-start') self._control_toolbar.show() self._toolbar_box.toolbar.insert(self._control_toolbar_button, -1) self._control_toolbar_button.hide() self.set_toolbar_box(self._toolbar_box) self._toolbar_box.show_all() self.connect('key_press_event', self.__key_press_event_cb) self.connect('playlist-finished', self.__playlist_finished_cb) # We want to be notified when the activity gets the focus or # loses it. When it is not active, we don't need to keep # reproducing the video self.connect('notify::active', self.__notify_active_cb) self._video_canvas = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) self._playlist_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.playlist_widget = PlayList() self.playlist_widget.connect('play-index', self.__play_index_cb) self.playlist_widget.connect('missing-tracks', self.__missing_tracks_cb) self.playlist_widget.set_size_request( Gdk.Screen.width() * PLAYLIST_WIDTH_PROP, 0) self.playlist_widget.show() self._playlist_box.pack_start(self.playlist_widget, expand=True, fill=True, padding=0) self._playlist_toolbar = Gtk.Toolbar() move_up = ToolButton("go-up") move_up.set_tooltip(_("Move up")) move_up.connect("clicked", self._move_up_cb) self._playlist_toolbar.insert(move_up, 0) move_down = ToolButton("go-down") move_down.set_tooltip(_("Move down")) move_down.connect("clicked", self._move_down_cb) self._playlist_toolbar.insert(move_down, 1) self._playlist_box.pack_end(self._playlist_toolbar, False, False, 0) self._video_canvas.pack_start(self._playlist_box, False, False, 0) # Create the player just once logging.debug('Instantiating GstPlayer') self.player = GstPlayer() self.player.connect('eos', self.__player_eos_cb) self.player.connect('error', self.__player_error_cb) self.player.connect('play', self.__player_play_cb) self.control = Controls(self, self._toolbar_box.toolbar, self._control_toolbar) self._separator = Gtk.SeparatorToolItem() self._separator.props.draw = False self._separator.set_expand(True) self._separator.show() self._toolbar_box.toolbar.insert(self._separator, -1) self._stop = StopButton(self) self._toolbar_box.toolbar.insert(self._stop, -1) self._empty_widget = Gtk.Label(label="") self._empty_widget.show() self.videowidget = VideoWidget() self.set_canvas(self._video_canvas) self._init_view_area() self.show_all() # need hide the playlist by default self._playlist_box.hide() self._configure_cb() self.player.init_view_area(self.videowidget) self._volume_monitor = Gio.VolumeMonitor.get() self._volume_monitor.connect('mount-added', self.__mount_added_cb) self._volume_monitor.connect('mount-removed', self.__mount_removed_cb) if handle.object_id is None: # The activity was launched from scratch. We need to show # the Empty Widget self.playlist_widget.hide() emptypanel.show(self, 'activity-jukebox', _('No media'), _('Choose media files'), self.control.show_picker_cb) self.control.check_if_next_prev() Gdk.Screen.get_default().connect('size-changed', self._configure_cb) def _move_up_cb(self, button): self.playlist_widget.move_up() def _move_down_cb(self, button): self.playlist_widget.move_down() def _configure_cb(self, event=None): self._toolbar_box.toolbar.remove(self._stop) self._toolbar_box.toolbar.remove(self._separator) if Gdk.Screen.width() < Gdk.Screen.height(): self._control_toolbar_button.show() self._control_toolbar_button.set_expanded(True) self.control.update_layout(landscape=False) self._toolbar_box.toolbar.insert(self._separator, -1) else: self._control_toolbar_button.set_expanded(False) self._control_toolbar_button.hide() self.control.update_layout(landscape=True) self._toolbar_box.toolbar.insert(self._stop, -1) def __notify_active_cb(self, widget, event): """Sugar notify us that the activity is becoming active or inactive. When we are inactive, we stop the player if it is reproducing a video. """ logging.debug('JukeboxActivity notify::active signal received') if self.player.player.props.current_uri is not None and \ self.player.playing_video(): if not self.player.is_playing() and self.props.active: self.player.play() if self.player.is_playing() and not self.props.active: self.player.pause() def _init_view_area(self): """ Use a notebook with two pages, one empty an another with the videowidget """ self.view_area = Gtk.Notebook() self.view_area.set_show_tabs(False) self.view_area.append_page(self._empty_widget, None) self.view_area.append_page(self.videowidget, None) self._video_canvas.pack_end(self.view_area, expand=True, fill=True, padding=0) def _switch_canvas(self, show_video): """Show or hide the video visualization in the canvas. When hidden, the canvas is filled with an empty widget to ensure redrawing. """ if show_video: self.view_area.set_current_page(1) else: self.view_area.set_current_page(0) self._video_canvas.queue_draw() def __key_press_event_cb(self, widget, event): keyname = Gdk.keyval_name(event.keyval) logging.info("Keyname Press: %s, time: %s", keyname, event.time) if self.title_entry.has_focus(): return False if keyname == "space": self.control._button_clicked_cb(None) return True def __playlist_finished_cb(self, widget): self._switch_canvas(show_video=False) self._view_toolbar._show_playlist.set_active(True) self.unfullscreen() # Select the first stream to be played when Play button will # be pressed self.playlist_widget.set_current_playing(0) self.control.check_if_next_prev() def songchange(self, direction): current_playing = self.playlist_widget.get_current_playing() if direction == 'prev' and current_playing > 0: self.play_index(current_playing - 1) elif direction == 'next' and \ current_playing < len(self.playlist_widget._items) - 1: self.play_index(current_playing + 1) else: self.emit('playlist-finished') def play_index(self, index): # README: this line is no more necessary because of the # .playing_video() method # self._switch_canvas(show_video=True) self.playlist_widget.set_current_playing(index) path = self.playlist_widget._items[index]['path'] if self.playlist_widget.check_available_media(path): if self.playlist_widget.is_from_journal(path): path = self.playlist_widget.get_path_from_journal(path) self.control.check_if_next_prev() self.player.set_uri(path) self.player.play() else: self.songchange('next') def __play_index_cb(self, widget, index, path): # README: this line is no more necessary because of the # .playing_video() method # self._switch_canvas(show_video=True) self.playlist_widget.set_current_playing(index) if self.playlist_widget.is_from_journal(path): path = self.playlist_widget.get_path_from_journal(path) self.control.check_if_next_prev() self.player.set_uri(path) self.player.play() def __player_eos_cb(self, widget): self.songchange('next') def _show_error_alert(self, title, msg=None): self._alert = ErrorAlert() self._alert.props.title = title if msg is not None: self._alert.props.msg = msg self.add_alert(self._alert) self._alert.connect('response', self._alert_cancel_cb) self._alert.show() def __mount_added_cb(self, volume_monitor, device): logging.debug('Mountpoint added. Checking...') self.remove_alert(self._alert) self.playlist_widget.update() def __mount_removed_cb(self, volume_monitor, device): logging.debug('Mountpoint removed. Checking...') self.remove_alert(self._alert) self.playlist_widget.update() def __missing_tracks_cb(self, widget, tracks): self._show_missing_tracks_alert(tracks) def _show_missing_tracks_alert(self, tracks): self._alert = Alert() title = _('%s tracks not found.') % len(tracks) self._alert.props.title = title icon = Icon(icon_name='dialog-cancel') self._alert.add_button(Gtk.ResponseType.CANCEL, _('Dismiss'), icon) icon.show() icon = Icon(icon_name='dialog-ok') self._alert.add_button(Gtk.ResponseType.APPLY, _('Details'), icon) icon.show() self.add_alert(self._alert) self._alert.connect( 'response', self.__missing_tracks_alert_response_cb, tracks) def __missing_tracks_alert_response_cb(self, alert, response_id, tracks): if response_id == Gtk.ResponseType.APPLY: vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) vbox.props.valign = Gtk.Align.CENTER label = Gtk.Label(label='') label.set_markup(_('<b>Missing tracks</b>')) vbox.pack_start(label, False, False, 15) for track in tracks: label = Gtk.Label(label=track['path']) vbox.add(label) _missing_tracks = Gtk.ScrolledWindow() _missing_tracks.add_with_viewport(vbox) _missing_tracks.show_all() self.view_area.append_page(_missing_tracks, None) self.view_area.set_current_page(2) self.remove_alert(alert) def _alert_cancel_cb(self, alert, response_id): self.remove_alert(alert) def __player_play_cb(self, widget): # Do not show the visualization widget if we are playing just # an audio stream def callback(): if self.player.playing_video(): self._switch_canvas(True) else: self._switch_canvas(False) return False # HACK: we need a timeout here because gstreamer returns # n-video = 0 if we call it immediately GObject.timeout_add(1000, callback) def __player_error_cb(self, widget, message, detail): self.player.stop() self.control.set_disabled() logging.error('ERROR MESSAGE: %s', message) logging.error('ERROR DETAIL: %s', detail) file_path = self.playlist_widget._items[ self.playlist_widget.get_current_playing()]['path'] mimetype = mime.get_for_file(file_path) title = _('Error') msg = _('This "%s" file can\'t be played') % mimetype self._switch_canvas(False) self._show_error_alert(title, msg) def can_close(self): # We need to put the Gst.State in NULL so gstreamer can # cleanup the pipeline self.player.stop() return True def read_file(self, file_path): """Load a file from the datastore on activity start.""" logging.debug('JukeBoxAtivity.read_file: %s', file_path) title = self.metadata['title'] self.playlist_widget.load_file(file_path, title) self._view_toolbar._show_playlist.set_active(True) def write_file(self, file_path): def write_playlist_to_file(file_path): """Open the file at file_path and write the playlist. It is saved in audio/x-mpegurl format. """ list_file = open(file_path, 'w') for uri in self.playlist_widget._items: list_file.write('#EXTINF:%s\n' % uri['title']) list_file.write('%s\n' % uri['path']) list_file.close() if not self.metadata['mime_type']: self.metadata['mime_type'] = 'audio/x-mpegurl' if self.metadata['mime_type'] == 'audio/x-mpegurl': write_playlist_to_file(file_path) else: if self._playlist_jobject is None: self._playlist_jobject = \ self.playlist_widget.create_playlist_jobject() # Add the playlist to the playlist jobject description. # This is only done if the activity was not started from a # playlist or from scratch: description = '' for uri in self.playlist_widget._items: description += '%s\n' % uri['title'] self._playlist_jobject.metadata['description'] = description write_playlist_to_file(self._playlist_jobject.file_path) datastore.write(self._playlist_jobject) def __go_fullscreen_cb(self, toolbar): self.fullscreen() def __toggle_playlist_cb(self, toolbar): if self._view_toolbar._show_playlist.get_active(): self._playlist_box.show_all() else: self._playlist_box.hide() self._video_canvas.queue_draw()
class BatchOperator(GObject.GObject): """ This class implements the course of actions that happens when clicking upon an BatchOperation (eg. Batch-Copy-Toolbar-button; Batch-Copy-To-Journal-button; Batch-Copy-To-Documents-button; Batch-Copy-To-Mounted-Drive-button; Batch-Copy-To-Clipboard-button; Batch-Erase-Button; """ def __init__(self, journalactivity, uid_list, alert_title, alert_message, operation_cb): GObject.GObject.__init__(self) self._journalactivity = journalactivity self._uid_list = uid_list[:] self._alert_title = alert_title self._alert_message = alert_message self._operation_cb = operation_cb self._show_confirmation_alert() def _show_confirmation_alert(self): self._journalactivity.freeze_ui() GLib.idle_add(self.__show_confirmation_alert_internal) def __show_confirmation_alert_internal(self): # Show a alert requesting confirmation before run the batch operation self._confirmation_alert = Alert() self._confirmation_alert.props.title = self._alert_title self._confirmation_alert.props.msg = self._alert_message stop_icon = Icon(icon_name='dialog-cancel') self._confirmation_alert.add_button(Gtk.ResponseType.CANCEL, _('Stop'), stop_icon) stop_icon.show() ok_icon = Icon(icon_name='dialog-ok') self._confirmation_alert.add_button(Gtk.ResponseType.OK, _('Continue'), ok_icon) ok_icon.show() self._journalactivity.add_alert(self._confirmation_alert) self._confirmation_alert.connect('response', self.__confirmation_response_cb) self._confirmation_alert.show() def __confirmation_response_cb(self, alert, response): if response == Gtk.ResponseType.CANCEL: self._journalactivity.unfreeze_ui() self._journalactivity.remove_alert(alert) # this is only in the case the operation already started # and the user want stop it. self._stop_batch_execution() elif hasattr(self, '_object_index') == False: self._object_index = 0 GLib.idle_add(self._operate_by_uid_internal) def _operate_by_uid_internal(self): # If there is still some uid left, proceed with the operation. # Else, proceed to post-operations. if self._object_index < len(self._uid_list): uid = self._uid_list[self._object_index] metadata = model.get(uid) title = None if 'title' in metadata: title = metadata['title'] if title is None or title == '': title = _('Untitled') alert_message = _('%(index)d of %(total)d : %(object_title)s') % { 'index': self._object_index + 1, 'total': len(self._uid_list), 'object_title': title} self._confirmation_alert.props.msg = alert_message GLib.idle_add(self._operate_per_metadata, metadata) else: self._finish_batch_execution() def _operate_per_metadata(self, metadata): self._operation_cb(metadata) # process the next self._object_index = self._object_index + 1 GLib.idle_add(self._operate_by_uid_internal) def _stop_batch_execution(self): self._object_index = len(self._uid_list) def _finish_batch_execution(self): del self._object_index self._journalactivity.unfreeze_ui() self._journalactivity.remove_alert(self._confirmation_alert) self._journalactivity.update_selected_items_ui()
class DevTutorActivity(activity.Activity): """DevTutorActivity class as specified in activity.info""" def __init__(self, handle): activity.Activity.__init__(self, handle) self.max_participants = 10 # toolbar with the new toolbar redesign toolbar_box = ToolbarBox() activity_button = ActivityToolbarButton(self) toolbar_box.toolbar.insert(activity_button, 0) activity_button.show() self.back_button = BackButton() self.back_button.connect('clicked', self.show_options1) toolbar_box.toolbar.insert(self.back_button, -1) self.back_button.show() separator = Gtk.SeparatorToolItem() separator.props.draw = False separator.set_expand(True) toolbar_box.toolbar.insert(separator, -1) separator.show() stop_button = StopButton(self) toolbar_box.toolbar.insert(stop_button, -1) stop_button.show() self.set_toolbar_box(toolbar_box) toolbar_box.show() self.show_options() self._logger = logging.getLogger('hellomesh-activity') self.hellotube = None # Shared session self.initiating = False # get the Presence Service self.pservice = presenceservice.get_instance() # Buddy object for you owner = self.pservice.get_owner() self.owner = owner self.connect('shared', self._shared_cb) self.connect('joined', self._joined_cb) def show_options1(self, data=None): self.show_options() def show_options(self): self.main_container = Gtk.VBox() self.add_padding() self.line1 = Gtk.HBox() button1 = Gtk.Button("Show modules") #button1.set_size_request(200,80) #self.line1.pack_start(button1, False, False, 0) self.line1.add(button1) button1.connect('clicked', self.show_modules, None) button1.get_child().modify_font(Pango.FontDescription("Sans 14")) button1.show() self.main_container.add(self.line1) self.line1.show() self.add_padding() self.line2 = Gtk.HBox() button2 = Gtk.Button("Show activities") #button2.set_size_request(200,80) #self.line2.pack_start(button2, False, False, 0) self.line2.add(button2) button2.connect('clicked', self.show_activity_list, None) button2.get_child().modify_font(Pango.FontDescription("Sans 14")) button2.show() self.main_container.add(self.line2) self.line2.show() self.add_padding() self.line3 = Gtk.HBox() text = Gtk.TextView() self.entry = Gtk.Entry() self.entry.set_sensitive(True) self.entry.connect('activate', self.entry_activate_cb) self.entry.show() self.line3.add(self.entry) self.main_container.add(self.line3) self.line3.show() self.set_canvas(self.main_container) self.main_container.show() def add_padding(self): self.line_space1 = Gtk.HBox() self.main_container.add(self.line_space1) self.line_space1.show() self.line_space2 = Gtk.HBox() self.main_container.add(self.line_space2) self.line_space2.show() def show_modules(self, sender, data=None): self.mod = ShowModules(self.set_canvas) self.mod.show_modules() def show_activity_list(self, sender, data=None): self.back_button.connect('clicked', self.show_options1) self.main_container = Gtk.VBox() self.add_padding() self.line1 = Gtk.HBox() button1 = Gtk.Button("Hello World activity") #button1.set_size_request(200,80) #self.line1.pack_start(button1, False, False, 0) self.line1.add(button1) button1.connect('clicked', self.show_labels_hello, None) button1.get_child().modify_font(Pango.FontDescription("Sans 14")) button1.show() self.main_container.add(self.line1) self.line1.show() self.add_padding() self.line2 = Gtk.HBox() button2 = Gtk.Button("Write activity") #button2.set_size_request(200,80) #self.line2.pack_start(button2, False, False, 0) self.line2.add(button2) button2.connect('clicked', self.show_labels_write, None) button2.get_child().modify_font(Pango.FontDescription("Sans 14")) button2.show() self.main_container.add(self.line2) self.line2.show() self.add_padding() self.set_canvas(self.main_container) self.main_container.show() def show_labels_hello(self, sender, data=None): self.back_button.connect('clicked', self.show_activity_list) self.main_container = Gtk.VBox() self.add_padding() self.line1 = Gtk.HBox() self.label1 = Gtk.Label(_("Hello World activity step 1 - call activity.__init__")) self.label1.set_line_wrap( True ) self.label1.modify_font(Pango.FontDescription("Sans 12")) self.line1.add(self.label1) self.label1.show() button1 = Gtk.Button("Show result") button1.set_size_request(200,80) self.line1.pack_start(button1, False, False, 0) button1.connect('clicked', self.hello_launch1, None) button1.get_child().modify_font(Pango.FontDescription("Sans 14")) button1.show() self.main_container.add(self.line1) self.line1.show() self.add_padding() self.line2 = Gtk.HBox() self.label2 = Gtk.Label(_("Hello Word activity step 2 - add toolbox")) self.label2.set_line_wrap( True ) self.label2.modify_font(Pango.FontDescription("Sans 12")) self.line2.add(self.label2) self.label2.show() button2 = Gtk.Button("Show result") button2.set_size_request(200,80) self.line2.pack_start(button2, False, False, 0) button2.connect('clicked', self.hello_launch2, None) button2.get_child().modify_font(Pango.FontDescription("Sans 14")) button2.show() self.main_container.add(self.line2) self.line2.show() self.add_padding() self.line3 = Gtk.HBox() self.label3 = Gtk.Label(_("Hello World activity step 3 - add hello world label")) self.label3.set_line_wrap( True ) self.label3.modify_font(Pango.FontDescription("Sans 12")) self.line3.add(self.label3) self.label3.show() button3 = Gtk.Button("Show result") button3.set_size_request(200,80) self.line3.pack_start(button3, False, False, 0) button3.connect('clicked', self.hello_launch3, None) button3.get_child().modify_font(Pango.FontDescription("Sans 14")) button3.show() self.main_container.add(self.line3) self.line3.show() self.add_padding() self.line4 = Gtk.HBox() self.label4 = Gtk.Label(_("Hello World activity step 4 - add rotate button")) self.label4.set_line_wrap( True ) self.label4.modify_font(Pango.FontDescription("Sans 12")) self.line4.add(self.label4) self.label4.show() button4 = Gtk.Button("Show result") button4.set_size_request(200,80) self.line4.pack_start(button4, False, False, 0) button4.connect('clicked', self.hello_launch4, None) button4.get_child().modify_font(Pango.FontDescription("Sans 14")) button4.show() self.main_container.add(self.line4) self.line4.show() self.add_padding() self.set_canvas(self.main_container) self.main_container.show() def show_labels_write(self, sender, data=None): self.back_button.connect('clicked', self.show_activity_list) self.main_container = Gtk.VBox() self.add_padding() self.line1 = Gtk.HBox() self.label1 = Gtk.Label(_("Write activity step 1 ")) self.label1.set_line_wrap( True ) self.label1.modify_font(Pango.FontDescription("Sans 12")) self.line1.add(self.label1) self.label1.show() button1 = Gtk.Button("Show result") button1.set_size_request(200,80) self.line1.pack_start(button1, False, False, 0) button1.connect('clicked', self.write_launch1, None) button1.get_child().modify_font(Pango.FontDescription("Sans 14")) button1.show() self.main_container.add(self.line1) self.line1.show() self.add_padding() self.line2 = Gtk.HBox() self.label2 = Gtk.Label(_("Write activity step 2")) self.label2.set_line_wrap( True ) self.label2.modify_font(Pango.FontDescription("Sans 12")) self.line2.add(self.label2) self.label2.show() button2 = Gtk.Button("Show result") button2.set_size_request(200,80) self.line2.pack_start(button2, False, False, 0) button2.connect('clicked', self.write_launch2, None) button2.get_child().modify_font(Pango.FontDescription("Sans 14")) button2.show() self.main_container.add(self.line2) self.line2.show() self.set_canvas(self.main_container) self.add_padding() self.line3 = Gtk.HBox() self.label3 = Gtk.Label(_("Write activity step 3")) self.label3.set_line_wrap( True ) self.label3.modify_font(Pango.FontDescription("Sans 12")) self.line3.add(self.label3) self.label3.show() button3 = Gtk.Button("Show result") button3.set_size_request(200,80) self.line3.pack_start(button3, False, False, 0) button3.connect('clicked', self.write_launch3, None) button3.get_child().modify_font(Pango.FontDescription("Sans 14")) button3.show() self.main_container.add(self.line3) self.line3.show() self.add_padding() self.set_canvas(self.main_container) self.main_container.show() def hello_launch1(self, sender, data=None): f = open('/tmp/1', 'w') os.putenv('TUTOR_CLASS','HelloWorldActivity') self.launch() def hello_launch2(self, sender, data=None): f = open('/tmp/2', 'w') self.hello_launch1(sender, data) def hello_launch3(self, sender, data=None): f = open('/tmp/3', 'w') self.hello_launch2(sender, data) def hello_launch4(self, sender, data=None): f = open('/tmp/4', 'w') self.hello_launch3(sender, data) def write_launch1(self, sender, data=None): f = open('/tmp/1', 'w') os.putenv('TUTOR_CLASS','AbiWordActivity') self.launch() def write_launch2(self, sender, data=None): f = open('/tmp/2', 'w') self.write_launch1(sender, data) def write_launch3(self, sender, data=None): f = open('/tmp/3', 'w') self.write_launch2(sender, data) def launch(self): subprocess.Popen(['sugar-launch', 'org.sugarlabs.DevTutor']) def entry_activate_cb(self, entry): """Handle the event when Enter is pressed in the Entry.""" text = entry.props.text if self.hellotube is not None: self.hellotube.SendText(text) def entry_text_update_cb(self, text): """Update Entry text when text received from others.""" self.entry.props.text = text def _alert(self, title, text=None): try: self.remove_alert(self.alert) finally: self.alert = Alert() self.alert.props.title = title self.alert.props.msg = text self.add_alert(self.alert) self.alert.connect('response', self._alert_cancel_cb) self.alert.show() def _alert_cancel_cb(self, alert, response_id): #self.remove_alert(alert) pass def _shared_cb(self, activity): self._logger.debug('My activity was shared') self.alert = Alert() self.alert.props.title = 'Shared Activity' self.alert.props.msg = 'Shared messages to be displayed here' self.add_alert(self.alert) self.initiating = True self._sharing_setup() self._logger.debug('This is my activity: making a tube...') id = self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube( SERVICE, {}) def _sharing_setup(self): if self.shared_activity is None: self._logger.error('Failed to share or join activity') return self.conn = self.shared_activity.telepathy_conn self.tubes_chan = self.shared_activity.telepathy_tubes_chan self.text_chan = self.shared_activity.telepathy_text_chan self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal( 'NewTube', self._new_tube_cb) self.shared_activity.connect('buddy-joined', self._buddy_joined_cb) self.shared_activity.connect('buddy-left', self._buddy_left_cb) self.entry.set_sensitive(True) self.entry.grab_focus() # Optional - included for example: # Find out who's already in the shared activity: for buddy in self.shared_activity.get_joined_buddies(): self._logger.debug('Buddy %s is already in the activity', buddy.props.nick) def _list_tubes_reply_cb(self, tubes): for tube_info in tubes: self._new_tube_cb(*tube_info) def _list_tubes_error_cb(self, e): self._logger.error('ListTubes() failed: %s', e) def _joined_cb(self, activity): if not self.shared_activity: return self._logger.debug('Joined an existing shared activity') self._alert('Joined', 'Joined a shared activity') self.initiating = False self._sharing_setup() self._logger.debug('This is not my activity: waiting for a tube...') self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes( reply_handler=self._list_tubes_reply_cb, error_handler=self._list_tubes_error_cb) def _new_tube_cb(self, id, initiator, type, service, params, state): self._logger.debug('New tube: ID=%d initator=%d type=%d service=%s ' 'params=%r state=%d', id, initiator, type, service, params, state) if (type == telepathy.TUBE_TYPE_DBUS and service == SERVICE): if state == telepathy.TUBE_STATE_LOCAL_PENDING: self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].AcceptDBusTube(id) tube_conn = TubeConnection(self.conn, self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES], id, group_iface=self.text_chan[telepathy.CHANNEL_INTERFACE_GROUP]) self.hellotube = TextSync(tube_conn, self.initiating, self.entry_text_update_cb, self._alert, self._get_buddy) def _buddy_joined_cb (self, activity, buddy): """Called when a buddy joins the shared activity. This doesn't do much here as HelloMesh doesn't have much functionality. It's up to you do do interesting things with the Buddy... """ self._logger.debug('Buddy %s joined', buddy.props.nick) self._alert('Buddy joined', '%s joined' % buddy.props.nick) def _buddy_left_cb (self, activity, buddy): """Called when a buddy leaves the shared activity. This doesn't do much here as HelloMesh doesn't have much functionality. It's up to you do do interesting things with the Buddy... """ self._logger.debug('Buddy %s left', buddy.props.nick) self._alert('Buddy left', '%s left' % buddy.props.nick) def _get_buddy(self, cs_handle): """Get a Buddy from a channel specific handle.""" self._logger.debug('Trying to find owner of handle %u...', cs_handle) group = self.text_chan[telepathy.CHANNEL_INTERFACE_GROUP] my_csh = group.GetSelfHandle() self._logger.debug('My handle in that group is %u', my_csh) if my_csh == cs_handle: handle = self.conn.GetSelfHandle() self._logger.debug('CS handle %u belongs to me, %u', cs_handle, handle) elif group.GetGroupFlags() & telepathy.CHANNEL_GROUP_FLAG_CHANNEL_SPECIFIC_HANDLES: handle = group.GetHandleOwners([cs_handle])[0] self._logger.debug('CS handle %u belongs to %u', cs_handle, handle) else: handle = cs_handle self._logger.debug('non-CS handle %u belongs to itself', handle) # XXX: deal with failure to get the handle owner assert handle != 0 return self.pservice.get_buddy_by_telepathy_handle( self.conn.service_name, self.conn.object_path, handle)