Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAneesh Dogra <lionaneesh@gmail.com>2012-12-12 16:17:38 (GMT)
committer Aneesh Dogra <lionaneesh@gmail.com>2012-12-12 16:18:15 (GMT)
commitc91b738d8e5016d344ebeab9ea6a9479eac3642a (patch)
tree73a256af981148d6dafcd43693472d841bd10be7
parent74b507fe5fcc8240294fd9eb28575a5f66aa7152 (diff)
Add gtk3 version of ReadEtexts 4.
-rw-r--r--New_Style_Toolbars_gtk3/MANIFEST9
-rw-r--r--New_Style_Toolbars_gtk3/ReadEtextsActivity4.py747
-rw-r--r--New_Style_Toolbars_gtk3/activity/activity.info9
-rw-r--r--New_Style_Toolbars_gtk3/activity/read-etexts.svg71
-rw-r--r--New_Style_Toolbars_gtk3/mybutton.py50
-rw-r--r--New_Style_Toolbars_gtk3/po/ReadEtextsIV.pot50
-rwxr-xr-xNew_Style_Toolbars_gtk3/setup.py23
-rw-r--r--New_Style_Toolbars_gtk3/toolbar.py134
8 files changed, 1093 insertions, 0 deletions
diff --git a/New_Style_Toolbars_gtk3/MANIFEST b/New_Style_Toolbars_gtk3/MANIFEST
new file mode 100644
index 0000000..f45e662
--- /dev/null
+++ b/New_Style_Toolbars_gtk3/MANIFEST
@@ -0,0 +1,9 @@
+toolbar.py
+setup.py
+
+
+activity/read-etexts.svg
+activity/activity.info
+ReadEtextsActivity4.py
+mybutton.py
+po/ReadEtextsIV.pot
diff --git a/New_Style_Toolbars_gtk3/ReadEtextsActivity4.py b/New_Style_Toolbars_gtk3/ReadEtextsActivity4.py
new file mode 100644
index 0000000..deeed3f
--- /dev/null
+++ b/New_Style_Toolbars_gtk3/ReadEtextsActivity4.py
@@ -0,0 +1,747 @@
+# 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
+# Copyright (C) 2012 Aneesh Dogra <lionaneesh@gmail.com>
+#
+# 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 US
+
+import os
+import re
+import logging
+import time
+import zipfile
+from gi.repository import Gtk
+from gi.repository import Gdk
+from gi.repository import Pango
+import dbus
+from gi.repository import GObject
+import telepathy
+from sugar3.activity import activity
+from sugar3.activity import widgets
+from sugar3.graphics.toolbutton import ToolButton
+from sugar3.graphics.toolbarbox import ToolbarBox
+from sugar3.graphics.toolbarbox import ToolbarButton
+from sugar3.activity.widgets import StopButton
+from toolbar import ViewToolbar
+from mybutton import MyActivityToolbarButton
+from sugar3.graphics.toggletoolbutton import ToggleToolButton
+from sugar3.graphics.menuitem import MenuItem
+from sugar3.graphics import style
+from sugar3 import network
+from sugar3.datastore import datastore
+from sugar3.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
+
+ self.create_new_toolbar()
+ self.scrolled_window = Gtk.ScrolledWindow()
+ self.scrolled_window.set_policy(Gtk.PolicyType.NEVER,
+ Gtk.PolicyType.AUTOMATIC)
+
+ 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_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, True, True, 0)
+ self.set_canvas(vbox)
+ vbox.show()
+
+ page = 0
+ self.clipboard = Gtk.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.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_new_toolbar(self):
+ toolbar_box = ToolbarBox()
+
+ activity_button = MyActivityToolbarButton(self)
+ toolbar_box.toolbar.insert(activity_button, 0)
+ activity_button.show()
+
+ self.edit_toolbar = widgets.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 = ToolButton('go-previous')
+ self.back.set_tooltip(_('Back'))
+ self.back.props.sensitive = False
+ self.back.connect('clicked', self.go_back_cb)
+ toolbar_box.toolbar.insert(self.back, -1)
+ self.back.show()
+
+ self.forward = ToolButton('go-next')
+ self.forward.set_tooltip(_('Forward'))
+ self.forward.props.sensitive = False
+ self.forward.connect('clicked', self.go_forward_cb)
+ toolbar_box.toolbar.insert(self.forward, -1)
+ self.forward.show()
+
+ num_page_item = Gtk.ToolItem()
+ self.num_page_entry = Gtk.Entry()
+ self.num_page_entry.set_text('0')
+ self.num_page_entry.set_alignment(1)
+ self.num_page_entry.connect('insert-text',
+ self.__new_num_page_entry_insert_text_cb)
+ self.num_page_entry.connect('activate',
+ self.__new_num_page_entry_activate_cb)
+ self.num_page_entry.set_width_chars(4)
+ 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 = Gtk.Label()
+
+ self.total_page_label.set_markup("<span foreground='#FFF'" \
+ " size='14000'></span>")
+
+ self.total_page_label.set_text(' / 0')
+ 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()
+
+ separator = Gtk.SeparatorToolItem()
+ separator.props.draw = False
+ separator.set_expand(True)
+ toolbar_box.toolbar.insert(separator, -1)
+ separator.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()
+
+ def __new_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 __new_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.total_pages:
+ new_page = self.total_pages - 1
+ elif new_page < 0:
+ new_page = 0
+
+ self.current_page = new_page
+ self.set_current_page(new_page)
+ self.show_page(new_page)
+ entry.props.text = str(new_page + 1)
+ self.update_nav_buttons()
+ page = new_page
+
+ def update_nav_buttons(self):
+ current_page = self.current_page
+ self.back.props.sensitive = current_page > 0
+ self.forward.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 set_total_pages(self, pages):
+ self.total_pages = pages
+
+ def set_current_page(self, page):
+ self.current_page = page
+ self.update_nav_buttons()
+
+ def keypress_cb(self, widget, event):
+ "Respond when the user presses one of the arrow keys"
+ keyname = 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.set_current_page(page)
+ self.show_page(page)
+ v_adjustment = self.scrolled_window.get_vadjustment()
+ v_adjustment.set_value(v_adjustment.get_upper() - \
+ v_adjustment.get_page_size())
+
+ def page_next(self):
+ global page
+ page=page+1
+ if page >= len(self.page_index): page=0
+ self.set_current_page(page)
+ self.show_page(page)
+ v_adjustment = self.scrolled_window.get_vadjustment()
+ v_adjustment.set_value(v_adjustment.get_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.get_value() == v_adjustment.get_upper() - \
+ v_adjustment.get_page_size():
+ self.page_next()
+ return
+ if v_adjustment.get_value() < v_adjustment.get_upper() - \
+ v_adjustment.get_page_size():
+ new_value = v_adjustment.get_value() + v_adjustment.step_increment
+ if new_value > v_adjustment.get_upper() - v_adjustment.get_page_size():
+ new_value = v_adjustment.get_upper() - v_adjustment.get_page_size()
+ v_adjustment.set_value(new_value)
+
+ def scroll_up(self):
+ v_adjustment = self.scrolled_window.get_vadjustment()
+ if v_adjustment.get_value() == v_adjustment.get_lower():
+ self.page_previous()
+ return
+ if v_adjustment.get_value() > v_adjustment.get_lower():
+ new_value = v_adjustment.get_value() - \
+ v_adjustment.step_increment
+ if new_value < v_adjustment.get_lower():
+ new_value = v_adjustment.get_lower()
+ v_adjustment.set_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)
+ self.set_total_pages(pagecount + 1)
+ self.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)
+ Gdk.threads_enter()
+ while Gtk.events_pending():
+ Gtk.main_iteration()
+ 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/New_Style_Toolbars_gtk3/activity/activity.info b/New_Style_Toolbars_gtk3/activity/activity.info
new file mode 100644
index 0000000..85968d7
--- /dev/null
+++ b/New_Style_Toolbars_gtk3/activity/activity.info
@@ -0,0 +1,9 @@
+[Activity]
+name = ReadEtexts IV
+bundle_id = net.flossmanuals.ReadETextsActivity4
+icon = read-etexts
+exec = sugar-activity ReadEtextsActivity4.ReadEtextsActivity
+show_launcher = no
+mime_types = text/plain;application/zip
+activity_version = 1
+license = GPLv2+
diff --git a/New_Style_Toolbars_gtk3/activity/read-etexts.svg b/New_Style_Toolbars_gtk3/activity/read-etexts.svg
new file mode 100644
index 0000000..5682ec8
--- /dev/null
+++ b/New_Style_Toolbars_gtk3/activity/read-etexts.svg
@@ -0,0 +1,71 @@
+<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd' [
+ <!ENTITY stroke_color "#000000">
+ <!ENTITY fill_color "#FFFFFF">
+]><svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="48px"
+ height="48px"
+ id="svg116"
+ sodipodi:version="0.32"
+ inkscape:version="0.46+devel"
+ sodipodi:docname="New document 4"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape">
+ <defs
+ id="defs118">
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 24 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="48 : 24 : 1"
+ inkscape:persp3d-origin="24 : 16 : 1"
+ id="perspective124" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="7"
+ inkscape:cx="24"
+ inkscape:cy="24"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ inkscape:grid-bbox="true"
+ inkscape:document-units="px"
+ inkscape:window-width="1024"
+ inkscape:window-height="698"
+ inkscape:window-x="0"
+ inkscape:window-y="25" />
+ <metadata
+ id="metadata121">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1"
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer">
+ <rect
+ style="fill:&fill_color;;stroke:&stroke_color;;stroke-opacity:1"
+ id="rect904"
+ width="36.142857"
+ height="32.142857"
+ x="4.1428571"
+ y="7.1428571" />
+ </g>
+</svg>
diff --git a/New_Style_Toolbars_gtk3/mybutton.py b/New_Style_Toolbars_gtk3/mybutton.py
new file mode 100644
index 0000000..75cc2f4
--- /dev/null
+++ b/New_Style_Toolbars_gtk3/mybutton.py
@@ -0,0 +1,50 @@
+# mybutton.py A version of ActivityToolbarButton that hides the "Keep"
+# button.
+
+# Copyright (C) 2010 James D. Simmons
+# Copyright (C) 2012 Aneesh Dogra <lionaneesh@gmail.com>
+#
+# 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 US
+import gconf
+
+from sugar3.graphics.toolbarbox import ToolbarButton
+from sugar3.activity.widgets import ActivityToolbar
+from sugar3.graphics.xocolor import XoColor
+from sugar3.graphics.icon import Icon
+from sugar3.bundle.activitybundle import ActivityBundle
+
+def _create_activity_icon(metadata):
+ if metadata.get('icon-color', ''):
+ color = XoColor(metadata['icon-color'])
+ else:
+ client = gconf.client_get_default()
+ color = XoColor(client.get_string('/desktop/sugar/user/color'))
+
+ from sugar3.activity.activity import get_bundle_path
+ bundle = ActivityBundle(get_bundle_path())
+ icon = Icon(file=bundle.get_icon(), xo_color=color)
+
+ return icon
+
+class MyActivityToolbarButton(ToolbarButton):
+
+ def __init__(self, activity, **kwargs):
+ toolbar = ActivityToolbar(activity, orientation_left=True)
+
+ ToolbarButton.__init__(self, page=toolbar, **kwargs)
+
+ icon = _create_activity_icon(activity.metadata)
+ self.set_icon_widget(icon)
+ icon.show()
diff --git a/New_Style_Toolbars_gtk3/po/ReadEtextsIV.pot b/New_Style_Toolbars_gtk3/po/ReadEtextsIV.pot
new file mode 100644
index 0000000..2aa9e2e
--- /dev/null
+++ b/New_Style_Toolbars_gtk3/po/ReadEtextsIV.pot
@@ -0,0 +1,50 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-12-12 21:48+0530\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: activity/activity.info:2
+msgid "ReadEtexts IV"
+msgstr ""
+
+#: toolbar.py:36 ReadEtextsActivity4.py:193
+msgid "Back"
+msgstr ""
+
+#: toolbar.py:42 ReadEtextsActivity4.py:200
+msgid "Forward"
+msgstr ""
+
+#: toolbar.py:113
+msgid "Zoom out"
+msgstr ""
+
+#: toolbar.py:118
+msgid "Zoom in"
+msgstr ""
+
+#: toolbar.py:128
+msgid "Fullscreen"
+msgstr ""
+
+#: ReadEtextsActivity4.py:647
+msgid "Failure"
+msgstr ""
+
+#: ReadEtextsActivity4.py:647
+msgid "Error getting document from tube"
+msgstr ""
diff --git a/New_Style_Toolbars_gtk3/setup.py b/New_Style_Toolbars_gtk3/setup.py
new file mode 100755
index 0000000..601d35c
--- /dev/null
+++ b/New_Style_Toolbars_gtk3/setup.py
@@ -0,0 +1,23 @@
+#!/usr/bin/env python
+
+# setup.py
+
+# Copyright (C) 2006, Red Hat, Inc.
+#
+# 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
+
+from sugar3.activity import bundlebuilder
+
+bundlebuilder.start()
diff --git a/New_Style_Toolbars_gtk3/toolbar.py b/New_Style_Toolbars_gtk3/toolbar.py
new file mode 100644
index 0000000..cac077f
--- /dev/null
+++ b/New_Style_Toolbars_gtk3/toolbar.py
@@ -0,0 +1,134 @@
+# toolbar.py The toolbars used by ReadEtextsActivity.
+#
+# Copyright (C) 2010, James Simmons.
+# Adapted from code Copyright (C) Red Hat Inc.
+# Copyright (C) 2012, Aneesh Dogra <lionaneesh@gmail.com>
+#
+# 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
+
+from gettext import gettext as _
+import re
+
+from gi.repository import Gtk
+from gi.repository import GObject
+
+from sugar3.graphics.toolbutton import ToolButton
+
+class ReadToolbar(Gtk.Toolbar):
+ __gtype_name__ = 'ReadToolbar'
+
+ def __init__(self):
+ Gtk.Toolbar.__init__(self)
+
+ self.back = ToolButton('go-previous')
+ self.back.set_tooltip(_('Back'))
+ self.back.props.sensitive = False
+ self.insert(self.back, -1)
+ self.back.show()
+
+ self.forward = ToolButton('go-next')
+ self.forward.set_tooltip(_('Forward'))
+ self.forward.props.sensitive = False
+ self.insert(self.forward, -1)
+ self.forward.show()
+
+ num_page_item = Gtk.ToolItem()
+
+ self.num_page_entry = Gtk.Entry()
+ self.num_page_entry.set_text('0')
+ self.num_page_entry.set_alignment(1)
+ self.num_page_entry.connect('insert-text',
+ self.num_page_entry_insert_text_cb)
+
+ self.num_page_entry.set_width_chars(4)
+
+ num_page_item.add(self.num_page_entry)
+ self.num_page_entry.show()
+
+ self.insert(num_page_item, -1)
+ num_page_item.show()
+
+ total_page_item = Gtk.ToolItem()
+
+ self.total_page_label = Gtk.Label()
+ self.total_page_label.set_markup("<span foreground='#FFF' size='14000'></span>")
+
+ self.total_page_label.set_text(' / 0')
+ total_page_item.add(self.total_page_label)
+ self.total_page_label.show()
+
+ self.insert(total_page_item, -1)
+ total_page_item.show()
+
+ 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 update_nav_buttons(self):
+ current_page = self.current_page
+ self.back.props.sensitive = current_page > 0
+ self.forward.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 set_total_pages(self, pages):
+ self.total_pages = pages
+
+ def set_current_page(self, page):
+ self.current_page = page
+ self.update_nav_buttons()
+
+class ViewToolbar(Gtk.Toolbar):
+ __gtype_name__ = 'ViewToolbar'
+
+ __gsignals__ = {
+ 'needs-update-size': (GObject.SIGNAL_RUN_FIRST,
+ GObject.TYPE_NONE,
+ ([])),
+ 'go-fullscreen': (GObject.SIGNAL_RUN_FIRST,
+ GObject.TYPE_NONE,
+ ([]))
+ }
+
+ def __init__(self):
+ Gtk.Toolbar.__init__(self)
+ self.zoom_out = ToolButton('zoom-out')
+ self.zoom_out.set_tooltip(_('Zoom out'))
+ self.insert(self.zoom_out, -1)
+ self.zoom_out.show()
+
+ self.zoom_in = ToolButton('zoom-in')
+ self.zoom_in.set_tooltip(_('Zoom in'))
+ self.insert(self.zoom_in, -1)
+ self.zoom_in.show()
+
+ spacer = Gtk.SeparatorToolItem()
+ spacer.props.draw = False
+ self.insert(spacer, -1)
+ spacer.show()
+
+ self.fullscreen = ToolButton('view-fullscreen')
+ self.fullscreen.set_tooltip(_('Fullscreen'))
+ self.fullscreen.connect('clicked', self.fullscreen_cb)
+ self.insert(self.fullscreen, -1)
+ self.fullscreen.show()
+
+ def fullscreen_cb(self, button):
+ self.emit('go-fullscreen')