Ejemplo n.º 1
0
    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()
Ejemplo n.º 2
0
    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']
Ejemplo n.º 3
0
    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()
Ejemplo n.º 4
0
    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)
Ejemplo n.º 6
0
 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()
Ejemplo n.º 8
0
    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']
Ejemplo n.º 9
0
    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()
Ejemplo n.º 10
0
    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()
Ejemplo n.º 11
0
    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()
Ejemplo n.º 12
0
    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()
Ejemplo n.º 13
0
 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()
Ejemplo n.º 14
0
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()
Ejemplo n.º 15
0
    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()
Ejemplo n.º 16
0
 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()
Ejemplo n.º 17
0
 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()
Ejemplo n.º 18
0
 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()
Ejemplo n.º 19
0
    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()
Ejemplo n.º 20
0
    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()
Ejemplo n.º 21
0
    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()
Ejemplo n.º 22
0
    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()
Ejemplo n.º 24
0
    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()
Ejemplo n.º 25
0
    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()
Ejemplo n.º 26
0
    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()
Ejemplo n.º 27
0
Archivo: gui.py Proyecto: junzy/sugar
    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()
Ejemplo n.º 28
0
    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)
Ejemplo n.º 30
0
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()
Ejemplo n.º 31
0
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)
Ejemplo n.º 32
0
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)
Ejemplo n.º 34
0
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)
Ejemplo n.º 37
0
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,
            ))
Ejemplo n.º 38
0
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)
Ejemplo n.º 39
0
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
Ejemplo n.º 40
0
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
Ejemplo n.º 41
0
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()
Ejemplo n.º 42
0
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()
Ejemplo n.º 43
0
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()
Ejemplo n.º 44
0
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)