diff options
author | James Simmons <jim@simmons.olpc> | 2010-03-04 01:53:50 (GMT) |
---|---|---|
committer | James Simmons <jim@simmons.olpc> | 2010-03-04 01:53:50 (GMT) |
commit | a542613c8c461e3a0e41bcaf4b6570c2c23f7099 (patch) | |
tree | bdabfb510b57bb0cdc6926310f7a6f9190d32e27 | |
parent | 54971136014bfc4909e1f14d7647eaefb365bacf (diff) |
Begin work on new toolbars example
-rw-r--r-- | ReadEtextsActivity4.py | 784 | ||||
-rw-r--r-- | activity/activity.info | 9 |
2 files changed, 789 insertions, 4 deletions
diff --git a/ReadEtextsActivity4.py b/ReadEtextsActivity4.py new file mode 100644 index 0000000..1711ed4 --- /dev/null +++ b/ReadEtextsActivity4.py @@ -0,0 +1,784 @@ +# +# ReadEtextsActivity4.py A version of ReadEtextsActivity that supports +# sharing ebooks over a Stream Tube in Telepathy and has both new and +# old style toolbars. + +# Copyright (C) 2010 James D. Simmons +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# + +import os +import re +import logging +import time +import zipfile +import gtk +import pango +import dbus +import gobject +import telepathy +from sugar.activity import activity + +from sugar.graphics.toolbutton import ToolButton + +_NEW_TOOLBAR_SUPPORT = True +try: + from sugar.graphics.toolbarbox import ToolbarBox + from sugar.graphics.toolbarbox import ToolbarButton + from sugar.activity.widgets import ActivityToolbarButton + from sugar.activity.widgets import StopButton + from toolbar import ViewToolbar +except: + _NEW_TOOLBAR_SUPPORT = False + from toolbar import ReadToolbar, ViewToolbar + +from sugar.graphics.toggletoolbutton import ToggleToolButton +from sugar.graphics.menuitem import MenuItem + +from sugar.graphics import style +from sugar import network +from sugar.datastore import datastore +from sugar.graphics.alert import NotifyAlert +from gettext import gettext as _ + +page=0 +PAGE_SIZE = 45 +TOOLBAR_READ = 2 + +logger = logging.getLogger('read-etexts2-activity') + +class ReadHTTPRequestHandler(network.ChunkedGlibHTTPRequestHandler): + """HTTP Request Handler for transferring document while collaborating. + + RequestHandler class that integrates with Glib mainloop. It writes + the specified file to the client in chunks, returning control to the + mainloop between chunks. + + """ + def translate_path(self, path): + """Return the filepath to the shared document.""" + return self.server.filepath + + +class ReadHTTPServer(network.GlibTCPServer): + """HTTP Server for transferring document while collaborating.""" + def __init__(self, server_address, filepath): + """Set up the GlibTCPServer with the ReadHTTPRequestHandler. + + filepath -- path to shared document to be served. + """ + self.filepath = filepath + network.GlibTCPServer.__init__(self, server_address, + ReadHTTPRequestHandler) + + +class ReadURLDownloader(network.GlibURLDownloader): + """URLDownloader that provides content-length and content-type.""" + + def get_content_length(self): + """Return the content-length of the download.""" + if self._info is not None: + return int(self._info.headers.get('Content-Length')) + + def get_content_type(self): + """Return the content-type of the download.""" + if self._info is not None: + return self._info.headers.get('Content-type') + return None + +READ_STREAM_SERVICE = 'read-etexts-activity-http' + +class ReadEtextsActivity(activity.Activity): + def __init__(self, handle): + "The entry point to the Activity" + global page + activity.Activity.__init__(self, handle) + + self.fileserver = None + self.object_id = handle.object_id + + if _NEW_TOOLBAR_SUPPORT: + toolbar_box = ToolbarBox() + + activity_button = ActivityToolbarButton(self) + toolbar_box.toolbar.insert(activity_button, 0) + activity_button.show() + + self.edit_toolbar = activity.EditToolbar() + self.edit_toolbar.undo.props.visible = False + self.edit_toolbar.redo.props.visible = False + self.edit_toolbar.separator.props.visible = False + self.edit_toolbar.copy.set_sensitive(False) + self.edit_toolbar.copy.connect('clicked', self.edit_toolbar_copy_cb) + self.edit_toolbar.paste.props.visible = False + + edit_toolbar_button = ToolbarButton( + page=self.edit_toolbar, + icon_name='toolbar-edit') + self.edit_toolbar.show() + toolbar_box.toolbar.insert(edit_toolbar_button, -1) + edit_toolbar_button.show() + + self.view_toolbar = ViewToolbar() + self.view_toolbar.connect('go-fullscreen', \ + self.view_toolbar_go_fullscreen_cb) + self.view_toolbar.zoom_in.connect('clicked', self.zoom_in_cb) + self.view_toolbar.zoom_out.connect('clicked', self.zoom_out_cb) + self.view_toolbar.show() + view_toolbar_button = ToolbarButton( + page=self.view_toolbar, + icon_name='toolbar-view') + toolbar_box.toolbar.insert(view_toolbar_button, -1) + view_toolbar_button.show() + + self._back_button = self._create_back_button() + toolbar_box.toolbar.insert(self._back_button, -1) + self._back_button.show() + + self._forward_button = self._create_forward_button() + toolbar_box.toolbar.insert(self._forward_button, -1) + self._forward_button.show() + + num_page_item = gtk.ToolItem() + self._num_page_entry = self._create_search() + num_page_item.add(self._num_page_entry) + self._num_page_entry.show() + toolbar_box.toolbar.insert(num_page_item, -1) + num_page_item.show() + + total_page_item = gtk.ToolItem() + self._total_page_label = self._create_total_page_label() + total_page_item.add(self._total_page_label) + self._total_page_label.show() + toolbar_box.toolbar.insert(total_page_item, -1) + total_page_item.show() + + spacer = gtk.SeparatorToolItem() + spacer.props.draw = False + toolbar_box.toolbar.insert(spacer, -1) + spacer.show() + + stop_button = StopButton(self) + stop_button.props.accelerator = '<Ctrl><Shift>Q' + toolbar_box.toolbar.insert(stop_button, -1) + stop_button.show() + + self.set_toolbar_box(toolbar_box) + toolbar_box.show() + + else: + toolbox = activity.ActivityToolbox(self) + activity_toolbar = toolbox.get_activity_toolbar() + activity_toolbar.keep.props.visible = False + + self.edit_toolbar = activity.EditToolbar() + self.edit_toolbar.undo.props.visible = False + self.edit_toolbar.redo.props.visible = False + self.edit_toolbar.separator.props.visible = False + self.edit_toolbar.copy.set_sensitive(False) + self.edit_toolbar.copy.connect('clicked', self.edit_toolbar_copy_cb) + self.edit_toolbar.paste.props.visible = False + toolbox.add_toolbar(_('Edit'), self.edit_toolbar) + self.edit_toolbar.show() + + self.read_toolbar = ReadToolbar() + toolbox.add_toolbar(_('Read'), self.read_toolbar) + self.read_toolbar.back.connect('clicked', self.go_back_cb) + self.read_toolbar.forward.connect('clicked', self.go_forward_cb) + self.read_toolbar.num_page_entry.connect('activate', \ + self.num_page_entry_activate_cb) + self.read_toolbar.show() + + self.view_toolbar = ViewToolbar() + toolbox.add_toolbar(_('View'), self.view_toolbar) + self.view_toolbar.connect('go-fullscreen', \ + self.view_toolbar_go_fullscreen_cb) + self.view_toolbar.zoom_in.connect('clicked', self.zoom_in_cb) + self.view_toolbar.zoom_out.connect('clicked', self.zoom_out_cb) + self.view_toolbar.show() + + self.set_toolbox(toolbox) + toolbox.show() + + self.scrolled_window = gtk.ScrolledWindow() + self.scrolled_window.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) + self.scrolled_window.props.shadow_type = gtk.SHADOW_NONE + + self.textview = gtk.TextView() + self.textview.set_editable(False) + self.textview.set_cursor_visible(False) + self.textview.set_left_margin(50) + self.textview.connect("key_press_event", self.keypress_cb) + + self.progressbar = gtk.ProgressBar() + self.progressbar.set_orientation(gtk.PROGRESS_LEFT_TO_RIGHT) + self.progressbar.set_fraction(0.0) + + self.scrolled_window.add(self.textview) + self.textview.show() + self.scrolled_window.show() + + vbox = gtk.VBox() + vbox.pack_start(self.progressbar, False, False, 10) + vbox.pack_start(self.scrolled_window) + self.set_canvas(vbox) + vbox.show() + + page = 0 + self.clipboard = gtk.Clipboard(display=gtk.gdk.display_get_default(), \ + selection="CLIPBOARD") + self.textview.grab_focus() + self.font_desc = pango.FontDescription("sans %d" % style.zoom(10)) + self.textview.modify_font(self.font_desc) + + buffer = self.textview.get_buffer() + self.markset_id = buffer.connect("mark-set", self.mark_set_cb) + + self.toolbox.set_current_toolbar(TOOLBAR_READ) + self.unused_download_tubes = set() + self.want_document = True + self.download_content_length = 0 + self.download_content_type = None + # Status of temp file used for write_file: + self.tempfile = None + self.close_requested = False + self.connect("shared", self.shared_cb) + + self.is_received_document = False + + if self._shared_activity and handle.object_id == None: + # We're joining, and we don't already have the document. + if self.get_shared(): + # Already joined for some reason, just get the document + self.joined_cb(self) + else: + # Wait for a successful join before trying to get the document + self.connect("joined", self.joined_cb) + + def _create_back_button(self): + back = ToolButton('go-previous') + back.set_tooltip(_('Back')) + back.props.sensitive = False + back.connect('clicked', self.go_back_cb) + return back + + def _create_forward_button(self): + forward = ToolButton('go-next') + forward.set_tooltip(_('Forward')) + forward.props.sensitive = False + forward.connect('clicked', self.go_forward_cb) + return forward + + def _create_search(self): + num_page_entry = gtk.Entry() + num_page_entry.set_text('0') + num_page_entry.set_alignment(1) + num_page_entry.connect('insert-text', + self.__num_page_entry_insert_text_cb) + num_page_entry.connect('activate', + self.__num_page_entry_activate_cb) + num_page_entry.set_width_chars(4) + return num_page_entry + + def _create_total_page_label(self): + total_page_label = gtk.Label() + + label_attributes = pango.AttrList() + label_attributes.insert(pango.AttrSize(14000, 0, -1)) + label_attributes.insert(pango.AttrForeground(65535, 65535, + 65535, 0, -1)) + total_page_label.set_attributes(label_attributes) + + total_page_label.set_text(' / 0') + return total_page_label + + def __num_page_entry_insert_text_cb(self, entry, text, length, position): + if not re.match('[0-9]', text): + entry.emit_stop_by_name('insert-text') + return True + return False + + def __num_page_entry_activate_cb(self, entry): + current_page = self.current_page + self._back_button.props.sensitive = current_page > 0 + self._forward_button.props.sensitive = \ + current_page < self.total_pages - 1 + + self._num_page_entry.props.text = str(current_page + 1) + self._total_page_label.props.label = \ + ' / ' + str(self.total_pages) + + def keypress_cb(self, widget, event): + "Respond when the user presses one of the arrow keys" + keyname = gtk.gdk.keyval_name(event.keyval) + print keyname + if keyname == 'plus': + self.font_increase() + return True + if keyname == 'minus': + self.font_decrease() + return True + if keyname == 'Page_Up' : + self.page_previous() + return True + if keyname == 'Page_Down': + self.page_next() + return True + if keyname == 'Up' or keyname == 'KP_Up' \ + or keyname == 'KP_Left': + self.scroll_up() + return True + if keyname == 'Down' or keyname == 'KP_Down' \ + or keyname == 'KP_Right': + self.scroll_down() + return True + return False + + def num_page_entry_activate_cb(self, entry): + global page + if entry.props.text: + new_page = int(entry.props.text) - 1 + else: + new_page = 0 + + if new_page >= self.read_toolbar.total_pages: + new_page = self.read_toolbar.total_pages - 1 + elif new_page < 0: + new_page = 0 + + self.read_toolbar.current_page = new_page + self.read_toolbar.set_current_page(new_page) + self.show_page(new_page) + entry.props.text = str(new_page + 1) + self.read_toolbar.update_nav_buttons() + page = new_page + + def go_back_cb(self, button): + self.page_previous() + + def go_forward_cb(self, button): + self.page_next() + + def page_previous(self): + global page + page=page-1 + if page < 0: page=0 + self.read_toolbar.set_current_page(page) + self.show_page(page) + v_adjustment = self.scrolled_window.get_vadjustment() + v_adjustment.value = v_adjustment.upper - v_adjustment.page_size + + def page_next(self): + global page + page=page+1 + if page >= len(self.page_index): page=0 + self.read_toolbar.set_current_page(page) + self.show_page(page) + v_adjustment = self.scrolled_window.get_vadjustment() + v_adjustment.value = v_adjustment.lower + + def zoom_in_cb(self, button): + self.font_increase() + + def zoom_out_cb(self, button): + self.font_decrease() + + def font_decrease(self): + font_size = self.font_desc.get_size() / 1024 + font_size = font_size - 1 + if font_size < 1: + font_size = 1 + self.font_desc.set_size(font_size * 1024) + self.textview.modify_font(self.font_desc) + + def font_increase(self): + font_size = self.font_desc.get_size() / 1024 + font_size = font_size + 1 + self.font_desc.set_size(font_size * 1024) + self.textview.modify_font(self.font_desc) + + def mark_set_cb(self, textbuffer, iter, textmark): + + if textbuffer.get_has_selection(): + begin, end = textbuffer.get_selection_bounds() + self.edit_toolbar.copy.set_sensitive(True) + else: + self.edit_toolbar.copy.set_sensitive(False) + + def edit_toolbar_copy_cb(self, button): + textbuffer = self.textview.get_buffer() + begin, end = textbuffer.get_selection_bounds() + copy_text = textbuffer.get_text(begin, end) + self.clipboard.set_text(copy_text) + + def view_toolbar_go_fullscreen_cb(self, view_toolbar): + self.fullscreen() + + def scroll_down(self): + v_adjustment = self.scrolled_window.get_vadjustment() + if v_adjustment.value == v_adjustment.upper - \ + v_adjustment.page_size: + self.page_next() + return + if v_adjustment.value < v_adjustment.upper - \ + v_adjustment.page_size: + new_value = v_adjustment.value + v_adjustment.step_increment + if new_value > v_adjustment.upper - v_adjustment.page_size: + new_value = v_adjustment.upper - v_adjustment.page_size + v_adjustment.value = new_value + + def scroll_up(self): + v_adjustment = self.scrolled_window.get_vadjustment() + if v_adjustment.value == v_adjustment.lower: + self.page_previous() + return + if v_adjustment.value > v_adjustment.lower: + new_value = v_adjustment.value - \ + v_adjustment.step_increment + if new_value < v_adjustment.lower: + new_value = v_adjustment.lower + v_adjustment.value = new_value + + def show_page(self, page_number): + global PAGE_SIZE, current_word + position = self.page_index[page_number] + self.etext_file.seek(position) + linecount = 0 + label_text = '\n\n\n' + textbuffer = self.textview.get_buffer() + while linecount < PAGE_SIZE: + line = self.etext_file.readline() + label_text = label_text + unicode(line, 'iso-8859-1') + linecount = linecount + 1 + label_text = label_text + '\n\n\n' + textbuffer.set_text(label_text) + self.textview.set_buffer(textbuffer) + + def save_extracted_file(self, zipfile, filename): + "Extract the file to a temp directory for viewing" + filebytes = zipfile.read(filename) + outfn = self.make_new_filename(filename) + if (outfn == ''): + return False + f = open(os.path.join(self.get_activity_root(), 'tmp', outfn), 'w') + try: + f.write(filebytes) + finally: + f.close() + + def get_saved_page_number(self): + global page + title = self.metadata.get('title', '') + if title == '' or not title[len(title)- 1].isdigit(): + page = 0 + else: + i = len(title) - 1 + newPage = '' + while (title[i].isdigit() and i > 0): + newPage = title[i] + newPage + i = i - 1 + if title[i] == 'P': + page = int(newPage) - 1 + else: + # not a page number; maybe a volume number. + page = 0 + + def save_page_number(self): + global page + title = self.metadata.get('title', '') + if title == '' or not title[len(title)- 1].isdigit(): + title = title + ' P' + str(page + 1) + else: + i = len(title) - 1 + while (title[i].isdigit() and i > 0): + i = i - 1 + if title[i] == 'P': + title = title[0:i] + 'P' + str(page + 1) + else: + title = title + ' P' + str(page + 1) + self.metadata['title'] = title + + def read_file(self, filename): + "Read the Etext file" + global PAGE_SIZE, page + + tempfile = os.path.join(self.get_activity_root(), 'instance', \ + 'tmp%i' % time.time()) + os.link(filename, tempfile) + self.tempfile = tempfile + + if zipfile.is_zipfile(filename): + self.zf = zipfile.ZipFile(filename, 'r') + self.book_files = self.zf.namelist() + self.save_extracted_file(self.zf, self.book_files[0]) + currentFileName = os.path.join(self.get_activity_root(), \ + 'tmp', self.book_files[0]) + else: + currentFileName = filename + + self.etext_file = open(currentFileName,"r") + self.page_index = [ 0 ] + pagecount = 0 + linecount = 0 + while self.etext_file: + line = self.etext_file.readline() + if not line: + break + linecount = linecount + 1 + if linecount >= PAGE_SIZE: + position = self.etext_file.tell() + self.page_index.append(position) + linecount = 0 + pagecount = pagecount + 1 + if filename.endswith(".zip"): + os.remove(currentFileName) + self.get_saved_page_number() + self.show_page(page) + if _NEW_TOOLBAR_SUPPORT: + self.total_pages = pagecount + 1 + else: + self.read_toolbar.set_total_pages(pagecount + 1) + self.read_toolbar.set_current_page(page) + + # We've got the document, so if we're a shared activity, offer it + if self.get_shared(): + self.watch_for_tubes() + self.share_document() + + def make_new_filename(self, filename): + partition_tuple = filename.rpartition('/') + return partition_tuple[2] + + def write_file(self, filename): + "Save meta data for the file." + if self.is_received_document: + # This document was given to us by someone, so we have + # to save it to the Journal. + self.etext_file.seek(0) + filebytes = self.etext_file.read() + print 'saving shared document' + f = open(filename, 'wb') + try: + f.write(filebytes) + finally: + f.close() + elif self.tempfile: + if self.close_requested: + os.link(self.tempfile, filename) + logger.debug("Removing temp file %s because we will close", \ + self.tempfile) + os.unlink(self.tempfile) + self.tempfile = None + else: + # skip saving empty file + raise NotImplementedError + + self.metadata['activity'] = self.get_bundle_id() + self.save_page_number() + + def can_close(self): + self.close_requested = True + return True + + def joined_cb(self, also_self): + """Callback for when a shared activity is joined. + + Get the shared document from another participant. + """ + self.watch_for_tubes() + gobject.idle_add(self.get_document) + + def get_document(self): + if not self.want_document: + return False + + # Assign a file path to download if one doesn't exist yet + if not self._jobject.file_path: + path = os.path.join(self.get_activity_root(), 'instance', + 'tmp%i' % time.time()) + else: + path = self._jobject.file_path + + # Pick an arbitrary tube we can try to download the document from + try: + tube_id = self.unused_download_tubes.pop() + except (ValueError, KeyError), e: + logger.debug('No tubes to get the document from right now: %s', + e) + return False + + # Avoid trying to download the document multiple times at once + self.want_document = False + gobject.idle_add(self.download_document, tube_id, path) + return False + + def download_document(self, tube_id, path): + chan = self._shared_activity.telepathy_tubes_chan + iface = chan[telepathy.CHANNEL_TYPE_TUBES] + addr = iface.AcceptStreamTube(tube_id, + telepathy.SOCKET_ADDRESS_TYPE_IPV4, + telepathy.SOCKET_ACCESS_CONTROL_LOCALHOST, 0, + utf8_strings=True) + logger.debug('Accepted stream tube: listening address is %r', \ + addr) + assert isinstance(addr, dbus.Struct) + assert len(addr) == 2 + assert isinstance(addr[0], str) + assert isinstance(addr[1], (int, long)) + assert addr[1] > 0 and addr[1] < 65536 + port = int(addr[1]) + + self.progressbar.show() + getter = ReadURLDownloader("http://%s:%d/document" + % (addr[0], port)) + getter.connect("finished", self.download_result_cb, tube_id) + getter.connect("progress", self.download_progress_cb, tube_id) + getter.connect("error", self.download_error_cb, tube_id) + logger.debug("Starting download to %s...", path) + getter.start(path) + self.download_content_length = getter.get_content_length() + self.download_content_type = getter.get_content_type() + return False + + def download_progress_cb(self, getter, bytes_downloaded, tube_id): + if self.download_content_length > 0: + logger.debug("Downloaded %u of %u bytes from tube %u...", + bytes_downloaded, self.download_content_length, + tube_id) + else: + logger.debug("Downloaded %u bytes from tube %u...", + bytes_downloaded, tube_id) + total = self.download_content_length + self.set_downloaded_bytes(bytes_downloaded, total) + gtk.gdk.threads_enter() + while gtk.events_pending(): + gtk.main_iteration() + gtk.gdk.threads_leave() + + def set_downloaded_bytes(self, bytes, total): + fraction = float(bytes) / float(total) + self.progressbar.set_fraction(fraction) + logger.debug("Downloaded percent", fraction) + + def clear_downloaded_bytes(self): + self.progressbar.set_fraction(0.0) + logger.debug("Cleared download bytes") + + def download_error_cb(self, getter, err, tube_id): + self.progressbar.hide() + logger.debug("Error getting document from tube %u: %s", + tube_id, err) + self.alert(_('Failure'), _('Error getting document from tube')) + self.want_document = True + self.download_content_length = 0 + self.download_content_type = None + gobject.idle_add(self.get_document) + + def download_result_cb(self, getter, tempfile, suggested_name, tube_id): + if self.download_content_type.startswith('text/html'): + # got an error page instead + self.download_error_cb(getter, 'HTTP Error', tube_id) + return + + del self.unused_download_tubes + + self.tempfile = tempfile + file_path = os.path.join(self.get_activity_root(), 'instance', + '%i' % time.time()) + logger.debug("Saving file %s to datastore...", file_path) + os.link(tempfile, file_path) + self._jobject.file_path = file_path + datastore.write(self._jobject, transfer_ownership=True) + + logger.debug("Got document %s (%s) from tube %u", + tempfile, suggested_name, tube_id) + self.is_received_document = True + self.read_file(tempfile) + self.save() + self.progressbar.hide() + + def shared_cb(self, activityid): + """Callback when activity shared. + + Set up to share the document. + + """ + # We initiated this activity and have now shared it, so by + # definition we have the file. + logger.debug('Activity became shared') + self.watch_for_tubes() + self.share_document() + + def share_document(self): + """Share the document.""" + h = hash(self._activity_id) + port = 1024 + (h % 64511) + logger.debug('Starting HTTP server on port %d', port) + self.fileserver = ReadHTTPServer(("", port), + self.tempfile) + + # Make a tube for it + chan = self._shared_activity.telepathy_tubes_chan + iface = chan[telepathy.CHANNEL_TYPE_TUBES] + self.fileserver_tube_id = iface.OfferStreamTube(READ_STREAM_SERVICE, + {}, + telepathy.SOCKET_ADDRESS_TYPE_IPV4, + ('127.0.0.1', dbus.UInt16(port)), + telepathy.SOCKET_ACCESS_CONTROL_LOCALHOST, 0) + + def watch_for_tubes(self): + """Watch for new tubes.""" + tubes_chan = self._shared_activity.telepathy_tubes_chan + + tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal('NewTube', + self.new_tube_cb) + 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, tube_id, initiator, tube_type, service, params, + state): + """Callback when a new tube becomes available.""" + logger.debug('New tube: ID=%d initator=%d type=%d service=%s ' + 'params=%r state=%d', tube_id, initiator, tube_type, + service, params, state) + if service == READ_STREAM_SERVICE: + logger.debug('I could download from that tube') + self.unused_download_tubes.add(tube_id) + # if no download is in progress, let's fetch the document + if self.want_document: + gobject.idle_add(self.get_document) + + def list_tubes_reply_cb(self, tubes): + """Callback when new tubes are available.""" + for tube_info in tubes: + self.new_tube_cb(*tube_info) + + def list_tubes_error_cb(self, e): + """Handle ListTubes error by logging.""" + logger.error('ListTubes() failed: %s', e) + + def alert(self, title, text=None): + alert = NotifyAlert(timeout=20) + alert.props.title = title + alert.props.msg = text + self.add_alert(alert) + alert.connect('response', self.alert_cancel_cb) + alert.show() + + def alert_cancel_cb(self, alert, response_id): + self.remove_alert(alert) + self.textview.grab_focus() diff --git a/activity/activity.info b/activity/activity.info index d7e12d3..cdd1d4b 100644 --- a/activity/activity.info +++ b/activity/activity.info @@ -1,8 +1,9 @@ [Activity] -name = Demoiselle -service_name = net.flossmanuals.Demoiselle +name = ReadEtexts II +service_name = net.flossmanuals.ReadETexts icon = read-etexts -exec = sugar-activity DemoiselleActivity.DemoiselleActivity -show_launcher = yes +exec = sugar-activity ReadEtextsActivity4.ReadEtexts +show_launcher = no + activity_version = 1 license = GPLv2+ |