Port from Python 2 to six
Signed-off-by: James Cameron <quozl@laptop.org>
This commit is contained in:
		
							parent
							
								
									6345da8e07
								
							
						
					
					
						commit
						aa8a5e70c4
					
				@ -26,4 +26,4 @@ check-po:
 | 
			
		||||
test: check-po
 | 
			
		||||
	pyflakes $(top_srcdir)
 | 
			
		||||
	pep8 $(top_srcdir)
 | 
			
		||||
	python -m sugar3.test.discover $(top_srcdir)/tests
 | 
			
		||||
	python3 -m sugar3.test.discover $(top_srcdir)/tests
 | 
			
		||||
 | 
			
		||||
@ -1 +1 @@
 | 
			
		||||
dist_bin_SCRIPTS = sugar-activity sugar-activity-web
 | 
			
		||||
dist_bin_SCRIPTS = sugar-activity sugar-activity-web sugar-activity3
 | 
			
		||||
 | 
			
		||||
@ -1,219 +1,5 @@
 | 
			
		||||
#!/usr/bin/env python2
 | 
			
		||||
 | 
			
		||||
# Copyright (C) 2006-2008, 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 activityinstance
 | 
			
		||||
 | 
			
		||||
import os
 | 
			
		||||
import sys
 | 
			
		||||
 | 
			
		||||
# Change the default encoding to avoid UnicodeDecodeError
 | 
			
		||||
# http://lists.sugarlabs.org/archive/sugar-devel/2012-August/038928.html
 | 
			
		||||
reload(sys)
 | 
			
		||||
sys.setdefaultencoding('utf-8')
 | 
			
		||||
 | 
			
		||||
import gettext
 | 
			
		||||
from optparse import OptionParser
 | 
			
		||||
 | 
			
		||||
import dbus
 | 
			
		||||
import dbus.service
 | 
			
		||||
from dbus.mainloop.glib import DBusGMainLoop
 | 
			
		||||
DBusGMainLoop(set_as_default=True)
 | 
			
		||||
 | 
			
		||||
from sugar3.activity import activityhandle
 | 
			
		||||
from sugar3 import config
 | 
			
		||||
from sugar3.bundle.activitybundle import ActivityBundle
 | 
			
		||||
from sugar3 import logger
 | 
			
		||||
 | 
			
		||||
from sugar3.bundle.bundle import MalformedBundleException
 | 
			
		||||
 | 
			
		||||
from distutils.dir_util import mkpath
 | 
			
		||||
import time
 | 
			
		||||
import hashlib
 | 
			
		||||
import random
 | 
			
		||||
 | 
			
		||||
def create_activity_instance(constructor, handle):
 | 
			
		||||
    activity = constructor(handle)
 | 
			
		||||
    activity.show()
 | 
			
		||||
    return activity
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_single_process_name(bundle_id):
 | 
			
		||||
    return bundle_id
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_single_process_path(bundle_id):
 | 
			
		||||
    return '/' + bundle_id.replace('.', '/')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SingleProcess(dbus.service.Object):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, name_service, constructor):
 | 
			
		||||
        self.constructor = constructor
 | 
			
		||||
 | 
			
		||||
        bus = dbus.SessionBus()
 | 
			
		||||
        bus_name = dbus.service.BusName(name_service, bus=bus)
 | 
			
		||||
        object_path = get_single_process_path(name_service)
 | 
			
		||||
        dbus.service.Object.__init__(self, bus_name, object_path)
 | 
			
		||||
 | 
			
		||||
    @dbus.service.method('org.laptop.SingleProcess', in_signature='a{sv}')
 | 
			
		||||
    def create(self, handle_dict):
 | 
			
		||||
        handle = activityhandle.create_from_dict(handle_dict)
 | 
			
		||||
        create_activity_instance(self.constructor, handle)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def main():
 | 
			
		||||
    usage = 'usage: %prog [options] [activity dir] [python class]'
 | 
			
		||||
    epilog = 'If you are running from a directory containing an Activity, ' \
 | 
			
		||||
             'the argument may be omitted.  Otherwise please provide either '\
 | 
			
		||||
             'a directory containing a Sugar Activity [activity dir], a '\
 | 
			
		||||
             '[python_class], or both.'
 | 
			
		||||
 | 
			
		||||
    parser = OptionParser(usage=usage, epilog=epilog)
 | 
			
		||||
    parser.add_option('-b', '--bundle-id', dest='bundle_id',
 | 
			
		||||
                      help='identifier of the activity bundle')
 | 
			
		||||
    parser.add_option('-a', '--activity-id', dest='activity_id',
 | 
			
		||||
                      help='identifier of the activity instance')
 | 
			
		||||
    parser.add_option('-o', '--object-id', dest='object_id',
 | 
			
		||||
                      help='identifier of the associated datastore object')
 | 
			
		||||
    parser.add_option('-u', '--uri', dest='uri',
 | 
			
		||||
                      help='URI to load')
 | 
			
		||||
    parser.add_option('-s', '--single-process', dest='single_process',
 | 
			
		||||
                      action='store_true',
 | 
			
		||||
                      help='start all the instances in the same process')
 | 
			
		||||
    parser.add_option('-i', '--invited', dest='invited',
 | 
			
		||||
                      action='store_true', default=False,
 | 
			
		||||
                      help='the activity is being launched for handling an '
 | 
			
		||||
                           'invite from the network')
 | 
			
		||||
    (options, args) = parser.parse_args()
 | 
			
		||||
 | 
			
		||||
    logger.start()
 | 
			
		||||
 | 
			
		||||
    activity_class = None
 | 
			
		||||
    if len(args) == 2:
 | 
			
		||||
        activity_class = args[1]
 | 
			
		||||
        os.chdir(args[0])
 | 
			
		||||
    elif len(args) == 1:
 | 
			
		||||
        if os.path.isdir(args[0]):
 | 
			
		||||
            os.chdir(args[0])
 | 
			
		||||
        else:
 | 
			
		||||
            activity_class = args[0]
 | 
			
		||||
 | 
			
		||||
    os.environ['SUGAR_BUNDLE_PATH'] = os.path.abspath(os.curdir)
 | 
			
		||||
 | 
			
		||||
    bundle_path = os.environ['SUGAR_BUNDLE_PATH']
 | 
			
		||||
    sys.path.insert(0, bundle_path)
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        bundle = ActivityBundle(bundle_path)
 | 
			
		||||
    except MalformedBundleException:
 | 
			
		||||
        parser.print_help()
 | 
			
		||||
        exit(0)
 | 
			
		||||
 | 
			
		||||
    if not activity_class:
 | 
			
		||||
        if bundle.get_command().startswith('sugar-activity'):
 | 
			
		||||
            activity_class = bundle.get_command().split(" ")[1]
 | 
			
		||||
 | 
			
		||||
    if 'SUGAR_VERSION' not in os.environ:
 | 
			
		||||
        profile_id = os.environ.get('SUGAR_PROFILE', 'default')
 | 
			
		||||
        home_dir = os.environ.get('SUGAR_HOME', os.path.expanduser('~/.sugar'))
 | 
			
		||||
        base = os.path.join(home_dir, profile_id)
 | 
			
		||||
        activity_root = os.path.join(base, bundle.get_bundle_id())
 | 
			
		||||
 | 
			
		||||
        instance_dir = os.path.join(activity_root, 'instance')
 | 
			
		||||
        mkpath(instance_dir)
 | 
			
		||||
 | 
			
		||||
        data_dir = os.path.join(activity_root, 'data')
 | 
			
		||||
        mkpath(data_dir)
 | 
			
		||||
 | 
			
		||||
        tmp_dir = os.path.join(activity_root, 'tmp')
 | 
			
		||||
        mkpath(tmp_dir)
 | 
			
		||||
 | 
			
		||||
        os.environ['SUGAR_ACTIVITY_ROOT'] = activity_root
 | 
			
		||||
        os.environ['SUGAR_BUNDLE_PATH'] = bundle.get_path()
 | 
			
		||||
 | 
			
		||||
    os.environ['SUGAR_BUNDLE_ID'] = bundle.get_bundle_id()
 | 
			
		||||
    os.environ['SUGAR_BUNDLE_NAME'] = bundle.get_name()
 | 
			
		||||
    os.environ['SUGAR_BUNDLE_VERSION'] = str(bundle.get_activity_version())
 | 
			
		||||
 | 
			
		||||
    # must be done early, some activities set translations globally, SL #3654
 | 
			
		||||
    activity_locale_path = os.environ.get("SUGAR_LOCALEDIR",
 | 
			
		||||
                                          config.locale_path)
 | 
			
		||||
 | 
			
		||||
    gettext.bindtextdomain(bundle.get_bundle_id(), activity_locale_path)
 | 
			
		||||
    gettext.bindtextdomain('sugar-toolkit-gtk3', config.locale_path)
 | 
			
		||||
    gettext.textdomain(bundle.get_bundle_id())
 | 
			
		||||
 | 
			
		||||
    splitted_module = activity_class.rsplit('.', 1)
 | 
			
		||||
    module_name = splitted_module[0]
 | 
			
		||||
    class_name = splitted_module[1]
 | 
			
		||||
 | 
			
		||||
    module = __import__(module_name)
 | 
			
		||||
    for comp in module_name.split('.')[1:]:
 | 
			
		||||
        module = getattr(module, comp)
 | 
			
		||||
 | 
			
		||||
    activity_constructor = getattr(module, class_name)
 | 
			
		||||
 | 
			
		||||
    if not options.activity_id:
 | 
			
		||||
        # Generate random hash
 | 
			
		||||
        data = '%s%s' % (time.time(), random.randint(10000, 100000))
 | 
			
		||||
        random_hash = hashlib.sha1(data).hexdigest()
 | 
			
		||||
        options.activity_id = random_hash
 | 
			
		||||
        options.bundle_id = bundle.get_bundle_id()
 | 
			
		||||
 | 
			
		||||
    activity_handle = activityhandle.ActivityHandle(
 | 
			
		||||
        activity_id=options.activity_id,
 | 
			
		||||
        object_id=options.object_id, uri=options.uri,
 | 
			
		||||
        invited=options.invited)
 | 
			
		||||
 | 
			
		||||
    if options.single_process is True:
 | 
			
		||||
        sessionbus = dbus.SessionBus()
 | 
			
		||||
 | 
			
		||||
        service_name = get_single_process_name(options.bundle_id)
 | 
			
		||||
        service_path = get_single_process_path(options.bundle_id)
 | 
			
		||||
 | 
			
		||||
        bus_object = sessionbus.get_object(
 | 
			
		||||
            'org.freedesktop.DBus', '/org/freedesktop/DBus')
 | 
			
		||||
        try:
 | 
			
		||||
            name = bus_object.GetNameOwner(
 | 
			
		||||
                service_name, dbus_interface='org.freedesktop.DBus')
 | 
			
		||||
        except dbus.DBusException:
 | 
			
		||||
            name = None
 | 
			
		||||
 | 
			
		||||
        if not name:
 | 
			
		||||
            SingleProcess(service_name, activity_constructor)
 | 
			
		||||
        else:
 | 
			
		||||
            try:
 | 
			
		||||
                single_process = sessionbus.get_object(service_name,
 | 
			
		||||
                                                       service_path)
 | 
			
		||||
                single_process.create(
 | 
			
		||||
                    activity_handle.get_dict(),
 | 
			
		||||
                    dbus_interface='org.laptop.SingleProcess')
 | 
			
		||||
 | 
			
		||||
                print 'Created %s in a single process.' % service_name
 | 
			
		||||
                sys.exit(0)
 | 
			
		||||
            except (TypeError, dbus.DBusException):
 | 
			
		||||
                print 'Could not communicate with the instance process,' \
 | 
			
		||||
                    'launching a new process'
 | 
			
		||||
 | 
			
		||||
    if hasattr(module, 'start'):
 | 
			
		||||
        module.start()
 | 
			
		||||
 | 
			
		||||
    instance = create_activity_instance(activity_constructor, activity_handle)
 | 
			
		||||
 | 
			
		||||
    if hasattr(instance, 'run_main_loop'):
 | 
			
		||||
        instance.run_main_loop()
 | 
			
		||||
 | 
			
		||||
main()
 | 
			
		||||
activityinstance.main()
 | 
			
		||||
 | 
			
		||||
@ -18,7 +18,7 @@
 | 
			
		||||
# Boston, MA 02111-1307, USA.
 | 
			
		||||
 | 
			
		||||
if [ "$SUGAR_USE_WEBKIT1" = "yes" ]; then
 | 
			
		||||
    exec sugar-activity sugar3.activity.webkit1.WebActivity $@
 | 
			
		||||
    exec sugar-activity3 sugar3.activity.webkit1.WebActivity $@
 | 
			
		||||
else
 | 
			
		||||
    exec sugar-activity sugar3.activity.webactivity.WebActivity $@
 | 
			
		||||
    exec sugar-activity3 sugar3.activity.webactivity.WebActivity $@
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										5
									
								
								bin/sugar-activity3
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										5
									
								
								bin/sugar-activity3
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,5 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
 | 
			
		||||
from sugar3.activity import activityinstance
 | 
			
		||||
 | 
			
		||||
activityinstance.main()
 | 
			
		||||
@ -15,7 +15,7 @@ GNOME_COMPILE_WARNINGS(maximum)
 | 
			
		||||
 | 
			
		||||
AC_PATH_PROG([GLIB_GENMARSHAL], [glib-genmarshal])
 | 
			
		||||
 | 
			
		||||
PYTHON=python2
 | 
			
		||||
PYTHON=python3
 | 
			
		||||
AM_PATH_PYTHON
 | 
			
		||||
 | 
			
		||||
PKG_CHECK_MODULES(EXT, gtk+-3.0 gdk-3.0 gdk-pixbuf-2.0 sm ice alsa
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										26
									
								
								doc/conf.py
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								doc/conf.py
									
									
									
									
									
								
							@ -226,25 +226,25 @@ htmlhelp_basename = 'SugarToolkitGTK3doc'
 | 
			
		||||
# -- Options for LaTeX output ---------------------------------------------
 | 
			
		||||
 | 
			
		||||
latex_elements = {
 | 
			
		||||
# The paper size ('letterpaper' or 'a4paper').
 | 
			
		||||
#'papersize': 'letterpaper',
 | 
			
		||||
    # The paper size ('letterpaper' or 'a4paper').
 | 
			
		||||
    #'papersize': 'letterpaper',
 | 
			
		||||
 | 
			
		||||
# The font size ('10pt', '11pt' or '12pt').
 | 
			
		||||
#'pointsize': '10pt',
 | 
			
		||||
    # The font size ('10pt', '11pt' or '12pt').
 | 
			
		||||
    #'pointsize': '10pt',
 | 
			
		||||
 | 
			
		||||
# Additional stuff for the LaTeX preamble.
 | 
			
		||||
#'preamble': '',
 | 
			
		||||
    # Additional stuff for the LaTeX preamble.
 | 
			
		||||
    #'preamble': '',
 | 
			
		||||
 | 
			
		||||
# Latex figure (float) alignment
 | 
			
		||||
#'figure_align': 'htbp',
 | 
			
		||||
    # Latex figure (float) alignment
 | 
			
		||||
    #'figure_align': 'htbp',
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Grouping the document tree into LaTeX files. List of tuples
 | 
			
		||||
# (source start file, target name, title,
 | 
			
		||||
#  author, documentclass [howto, manual, or own class]).
 | 
			
		||||
latex_documents = [
 | 
			
		||||
  (master_doc, 'SugarToolkitGTK3.tex', u'Sugar Toolkit GTK3 Documentation',
 | 
			
		||||
   u'Sugar Labs', 'manual'),
 | 
			
		||||
    (master_doc, 'SugarToolkitGTK3.tex', u'Sugar Toolkit GTK3 Documentation',
 | 
			
		||||
     u'Sugar Labs', 'manual'),
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
# The name of an image file (relative to this directory) to place at the top of
 | 
			
		||||
@ -287,9 +287,9 @@ man_pages = [
 | 
			
		||||
# (source start file, target name, title, author,
 | 
			
		||||
#  dir menu entry, description, category)
 | 
			
		||||
texinfo_documents = [
 | 
			
		||||
  (master_doc, 'SugarToolkitGTK3', u'Sugar Toolkit GTK3 Documentation',
 | 
			
		||||
   author, 'SugarToolkitGTK3', 'One line description of project.',
 | 
			
		||||
   'Miscellaneous'),
 | 
			
		||||
    (master_doc, 'SugarToolkitGTK3', u'Sugar Toolkit GTK3 Documentation',
 | 
			
		||||
     author, 'SugarToolkitGTK3', 'One line description of project.',
 | 
			
		||||
     'Miscellaneous'),
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
# Documents to append as an appendix to all manuals.
 | 
			
		||||
 | 
			
		||||
@ -21,11 +21,11 @@ box.pack_start(alert, False, False, 0)
 | 
			
		||||
# Called when an alert object throws a response event.
 | 
			
		||||
def __alert_response_cb(alert, response_id):
 | 
			
		||||
    if response_id is Gtk.ResponseType.OK:
 | 
			
		||||
        print 'Continue Button was clicked.'
 | 
			
		||||
        print('Continue Button was clicked.')
 | 
			
		||||
    elif response_id is Gtk.ResponseType.CANCEL:
 | 
			
		||||
        print 'Cancel Button was clicked.'
 | 
			
		||||
        print('Cancel Button was clicked.')
 | 
			
		||||
    elif response_id == -1:
 | 
			
		||||
        print 'Timeout occurred'
 | 
			
		||||
        print('Timeout occurred')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
alert.connect('response', __alert_response_cb)
 | 
			
		||||
 | 
			
		||||
@ -23,7 +23,7 @@ class _Animation(animator.Animation):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def __animation_completed_cb(anim):
 | 
			
		||||
    print 'Animation completed'
 | 
			
		||||
    print('Animation completed')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
w = Gtk.Window()
 | 
			
		||||
 | 
			
		||||
@ -24,7 +24,7 @@ separator.show()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def color_changed_cb(button, pspec):
 | 
			
		||||
    print button.get_color()
 | 
			
		||||
    print(button.get_color())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
color_button = ColorToolButton()
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,7 @@ set_theme()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def __combo_changed_cb(combo):
 | 
			
		||||
    print 'Combo changed to %r' % combo.get_value()
 | 
			
		||||
    print('Combo changed to %r' % combo.get_value())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
w = Gtk.Window()
 | 
			
		||||
 | 
			
		||||
@ -13,14 +13,14 @@ class MyCellRenderer(Gtk.CellRenderer):
 | 
			
		||||
        Gtk.CellRenderer.__init__(self)
 | 
			
		||||
 | 
			
		||||
    def __del__(self):
 | 
			
		||||
        print "cellrenderer destroy"
 | 
			
		||||
        print("cellrenderer destroy")
 | 
			
		||||
 | 
			
		||||
    def do_render(self, cairo_t, widget, background_area, cell_area, flags):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def window_destroy_cb(*kwargs):
 | 
			
		||||
    print "window destroy"
 | 
			
		||||
    print("window destroy")
 | 
			
		||||
    Gtk.main_quit()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -30,7 +30,7 @@ window.show()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def treeview_destroy_cb(*kwargs):
 | 
			
		||||
    print "treeview destroy"
 | 
			
		||||
    print("treeview destroy")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
treeview = Gtk.TreeView()
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
#!/usr/bin/python
 | 
			
		||||
#!/usr/bin/python3
 | 
			
		||||
from gi.repository import Gtk
 | 
			
		||||
 | 
			
		||||
import common
 | 
			
		||||
 | 
			
		||||
@ -6,11 +6,11 @@ set_theme()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def __go_next_cb(entry, icon_pos, data=None):
 | 
			
		||||
    print 'Go next'
 | 
			
		||||
    print('Go next')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def __entry_activate_cb(widget, data=None):
 | 
			
		||||
    print 'Entry activate'
 | 
			
		||||
    print('Entry activate')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
w = Gtk.Window()
 | 
			
		||||
 | 
			
		||||
@ -19,7 +19,7 @@ box.show()
 | 
			
		||||
def echo(button, label):
 | 
			
		||||
    if not button.props.active:
 | 
			
		||||
        return
 | 
			
		||||
    print label
 | 
			
		||||
    print(label)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
palette = RadioPalette()
 | 
			
		||||
 | 
			
		||||
@ -12,12 +12,12 @@ import common
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _scroll_start_cb(event, treeview, invoker):
 | 
			
		||||
    print "Scroll starts"
 | 
			
		||||
    print("Scroll starts")
 | 
			
		||||
    invoker.detach()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _scroll_end_cb(event, treeview, invoker):
 | 
			
		||||
    print "Scroll ends"
 | 
			
		||||
    print("Scroll ends")
 | 
			
		||||
    invoker.attach_treeview(treeview)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -30,7 +30,7 @@ data_dir = os.getenv('GTK_DATA_PREFIX', '/usr/')
 | 
			
		||||
 | 
			
		||||
iconlist = os.listdir(os.path.join(data_dir,
 | 
			
		||||
                                   'share/icons/sugar/scalable/actions/'))
 | 
			
		||||
print "Displaying %s icons" % len(iconlist)
 | 
			
		||||
print("Displaying %s icons" % len(iconlist))
 | 
			
		||||
for icon in iconlist:
 | 
			
		||||
    icon = os.path.basename(icon)
 | 
			
		||||
    icon = icon[:icon.find('.')]
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
#!/usr/bin/python
 | 
			
		||||
#!/usr/bin/python3
 | 
			
		||||
from gi.repository import Gtk
 | 
			
		||||
 | 
			
		||||
from sugar3.graphics.icon import Icon
 | 
			
		||||
 | 
			
		||||
@ -21,7 +21,7 @@ toolbar_box.toolbar.insert(separator, -1)
 | 
			
		||||
def __clicked_cb(button):
 | 
			
		||||
    n = int(button.get_tooltip())
 | 
			
		||||
    button.set_tooltip(str(n + 1))
 | 
			
		||||
    print "tool button click count %d" % n
 | 
			
		||||
    print("tool button click count %d" % n)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
tool_button = ToolButton(icon_name='view-radial', tooltip='0')
 | 
			
		||||
 | 
			
		||||
@ -2,6 +2,7 @@ sugardir = $(pythondir)/sugar3/activity
 | 
			
		||||
sugar_PYTHON =              \
 | 
			
		||||
	__init__.py             \
 | 
			
		||||
	activity.py             \
 | 
			
		||||
	activityinstance.py     \
 | 
			
		||||
	activityfactory.py      \
 | 
			
		||||
	activityhandle.py       \
 | 
			
		||||
	activityservice.py      \
 | 
			
		||||
 | 
			
		||||
@ -158,6 +158,7 @@ Hint: A good and simple activity to learn from is the Read activity.
 | 
			
		||||
You may copy it and use it as a template.
 | 
			
		||||
'''
 | 
			
		||||
 | 
			
		||||
import six
 | 
			
		||||
import gettext
 | 
			
		||||
import logging
 | 
			
		||||
import os
 | 
			
		||||
@ -165,7 +166,6 @@ import signal
 | 
			
		||||
import time
 | 
			
		||||
from hashlib import sha1
 | 
			
		||||
from functools import partial
 | 
			
		||||
import StringIO
 | 
			
		||||
import cairo
 | 
			
		||||
import json
 | 
			
		||||
 | 
			
		||||
@ -206,7 +206,9 @@ from errno import EEXIST
 | 
			
		||||
 | 
			
		||||
from gi.repository import SugarExt
 | 
			
		||||
 | 
			
		||||
_ = lambda msg: gettext.dgettext('sugar-toolkit-gtk3', msg)
 | 
			
		||||
 | 
			
		||||
def _(msg): return gettext.dgettext('sugar-toolkit-gtk3', msg)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
SCOPE_PRIVATE = 'private'
 | 
			
		||||
SCOPE_INVITE_ONLY = 'invite'  # shouldn't be shown in UI, it's implicit
 | 
			
		||||
@ -881,7 +883,7 @@ class Activity(Window, Gtk.Container):
 | 
			
		||||
        cr.set_source_surface(screenshot_surface)
 | 
			
		||||
        cr.paint()
 | 
			
		||||
 | 
			
		||||
        preview_str = StringIO.StringIO()
 | 
			
		||||
        preview_str = six.BytesIO()
 | 
			
		||||
        preview_surface.write_to_png(preview_str)
 | 
			
		||||
        return preview_str.getvalue()
 | 
			
		||||
 | 
			
		||||
@ -919,7 +921,7 @@ class Activity(Window, Gtk.Container):
 | 
			
		||||
 | 
			
		||||
        buddies_dict = self._get_buddies()
 | 
			
		||||
        if buddies_dict:
 | 
			
		||||
            self.metadata['buddies_id'] = json.dumps(buddies_dict.keys())
 | 
			
		||||
            self.metadata['buddies_id'] = json.dumps(list(buddies_dict.keys()))
 | 
			
		||||
            self.metadata['buddies'] = json.dumps(self._get_buddies())
 | 
			
		||||
 | 
			
		||||
        # update spent time before saving
 | 
			
		||||
@ -1103,8 +1105,9 @@ class Activity(Window, Gtk.Container):
 | 
			
		||||
            raise RuntimeError('Activity %s already shared.' %
 | 
			
		||||
                               self._activity_id)
 | 
			
		||||
        verb = private and 'private' or 'public'
 | 
			
		||||
        logging.debug('Requesting %s share of activity %s.' % (verb,
 | 
			
		||||
                      self._activity_id))
 | 
			
		||||
        logging.debug(
 | 
			
		||||
            'Requesting %s share of activity %s.' %
 | 
			
		||||
            (verb, self._activity_id))
 | 
			
		||||
        pservice = presenceservice.get_instance()
 | 
			
		||||
        pservice.connect('activity-shared', self.__share_cb)
 | 
			
		||||
        pservice.share_activity(self, private=private)
 | 
			
		||||
@ -1197,8 +1200,8 @@ class Activity(Window, Gtk.Container):
 | 
			
		||||
        if response_id == Gtk.ResponseType.OK:
 | 
			
		||||
            title = alert.entry.get_text()
 | 
			
		||||
            if self._is_resumed and \
 | 
			
		||||
                title == self._original_title:
 | 
			
		||||
                    datastore.delete(self._jobject_old.get_object_id())
 | 
			
		||||
                    title == self._original_title:
 | 
			
		||||
                datastore.delete(self._jobject_old.get_object_id())
 | 
			
		||||
            self._jobject.metadata['title'] = title
 | 
			
		||||
            self._do_close(False)
 | 
			
		||||
 | 
			
		||||
@ -1222,7 +1225,7 @@ class Activity(Window, Gtk.Container):
 | 
			
		||||
        label = _('Save new')
 | 
			
		||||
        tip = _('Save a new journal entry')
 | 
			
		||||
        if self._is_resumed and \
 | 
			
		||||
            title == self._original_title:
 | 
			
		||||
                title == self._original_title:
 | 
			
		||||
            label = _('Save')
 | 
			
		||||
            tip = _('Save into the old journal entry')
 | 
			
		||||
 | 
			
		||||
@ -1244,7 +1247,7 @@ class Activity(Window, Gtk.Container):
 | 
			
		||||
        if not skip_save:
 | 
			
		||||
            try:
 | 
			
		||||
                self.save()
 | 
			
		||||
            except:
 | 
			
		||||
            except BaseException:
 | 
			
		||||
                # pylint: disable=W0702
 | 
			
		||||
                logging.exception('Error saving activity object to datastore')
 | 
			
		||||
                self._show_keep_failed_dialog()
 | 
			
		||||
@ -1306,12 +1309,12 @@ class Activity(Window, Gtk.Container):
 | 
			
		||||
 | 
			
		||||
    def __realize_cb(self, window):
 | 
			
		||||
        display_name = Gdk.Display.get_default().get_name()
 | 
			
		||||
        if ':' in display_name: 
 | 
			
		||||
        if ':' in display_name:
 | 
			
		||||
            # X11 for sure; this only works in X11
 | 
			
		||||
            xid = window.get_window().get_xid()
 | 
			
		||||
            SugarExt.wm_set_bundle_id(xid, self.get_bundle_id())
 | 
			
		||||
            SugarExt.wm_set_activity_id(xid, str(self._activity_id))
 | 
			
		||||
        elif display_name is 'Broadway': 
 | 
			
		||||
        elif display_name is 'Broadway':
 | 
			
		||||
            # GTK3's HTML5 backend
 | 
			
		||||
            # This is needed so that the window takes the whole browser window
 | 
			
		||||
            self.maximize()
 | 
			
		||||
@ -1432,7 +1435,7 @@ class _ClientHandler(dbus.service.Object, DBusProperties):
 | 
			
		||||
        }
 | 
			
		||||
        filter_dict = dbus.Dictionary(filters, signature='sv')
 | 
			
		||||
        logging.debug('__get_filters_cb %r' % dbus.Array([filter_dict],
 | 
			
		||||
                      signature='a{sv}'))
 | 
			
		||||
                                                         signature='a{sv}'))
 | 
			
		||||
        return dbus.Array([filter_dict], signature='a{sv}')
 | 
			
		||||
 | 
			
		||||
    @dbus.service.method(dbus_interface=CLIENT_HANDLER,
 | 
			
		||||
@ -1448,9 +1451,10 @@ class _ClientHandler(dbus.service.Object, DBusProperties):
 | 
			
		||||
                handle_type = properties[CHANNEL + '.TargetHandleType']
 | 
			
		||||
                if channel_type == CHANNEL_TYPE_TEXT:
 | 
			
		||||
                    self._got_channel_cb(connection, object_path, handle_type)
 | 
			
		||||
        except Exception, e:
 | 
			
		||||
        except Exception as e:
 | 
			
		||||
            logging.exception(e)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
_session = None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1503,7 +1507,7 @@ def get_activity_root():
 | 
			
		||||
        activity_root = env.get_profile_path(os.environ['SUGAR_BUNDLE_ID'])
 | 
			
		||||
        try:
 | 
			
		||||
            os.mkdir(activity_root)
 | 
			
		||||
        except OSError, e:
 | 
			
		||||
        except OSError as e:
 | 
			
		||||
            if e.errno != EEXIST:
 | 
			
		||||
                raise e
 | 
			
		||||
        return activity_root
 | 
			
		||||
 | 
			
		||||
@ -23,7 +23,6 @@ the moment there is no reason to stabilize this API.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
import dbus
 | 
			
		||||
from gi.repository import GObject
 | 
			
		||||
from gi.repository import GLib
 | 
			
		||||
@ -53,7 +52,7 @@ except ValueError:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _close_fds():
 | 
			
		||||
    for i in xrange(3, MAXFD):
 | 
			
		||||
    for i in range(3, MAXFD):
 | 
			
		||||
        try:
 | 
			
		||||
            os.close(i)
 | 
			
		||||
        # pylint: disable=W0704
 | 
			
		||||
@ -69,7 +68,7 @@ def create_activity_id():
 | 
			
		||||
def _mkdir(path):
 | 
			
		||||
    try:
 | 
			
		||||
        os.mkdir(path)
 | 
			
		||||
    except OSError, e:
 | 
			
		||||
    except OSError as e:
 | 
			
		||||
        if e.errno != EEXIST:
 | 
			
		||||
            raise e
 | 
			
		||||
 | 
			
		||||
@ -137,10 +136,10 @@ def open_log_file(activity):
 | 
			
		||||
    while True:
 | 
			
		||||
        path = env.get_logs_path('%s-%s.log' % (activity.get_bundle_id(), i))
 | 
			
		||||
        try:
 | 
			
		||||
            fd = os.open(path, os.O_EXCL | os.O_CREAT | os.O_WRONLY, 0644)
 | 
			
		||||
            f = os.fdopen(fd, 'w', 0)
 | 
			
		||||
            fd = os.open(path, os.O_EXCL | os.O_CREAT | os.O_WRONLY, 0o644)
 | 
			
		||||
            f = os.fdopen(fd, 'w')
 | 
			
		||||
            return (path, f)
 | 
			
		||||
        except OSError, e:
 | 
			
		||||
        except OSError as e:
 | 
			
		||||
            if e.errno == EEXIST:
 | 
			
		||||
                i += 1
 | 
			
		||||
            elif e.errno == ENOSPC:
 | 
			
		||||
@ -225,7 +224,7 @@ class ActivityCreationHandler(GObject.GObject):
 | 
			
		||||
                              self._handle.object_id, self._handle.uri,
 | 
			
		||||
                              self._handle.invited)
 | 
			
		||||
 | 
			
		||||
        dev_null = file('/dev/null', 'r')
 | 
			
		||||
        dev_null = open('/dev/null', 'r')
 | 
			
		||||
        child = subprocess.Popen([str(s) for s in command],
 | 
			
		||||
                                 env=environ,
 | 
			
		||||
                                 cwd=str(self._bundle.get_path()),
 | 
			
		||||
 | 
			
		||||
@ -24,26 +24,26 @@ journal object id's,
 | 
			
		||||
class ActivityHandle(object):
 | 
			
		||||
    '''
 | 
			
		||||
    Data structure storing simple activity metadata
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        activity_id (string): unique id for the activity to be
 | 
			
		||||
        created
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        object_id (string): identity of the journal object
 | 
			
		||||
        associated with the activity.
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        When you resume an activity from the journal
 | 
			
		||||
        the object_id will be passed in. It is optional
 | 
			
		||||
        since new activities does not have an
 | 
			
		||||
        associated object.
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        uri (string): URI associated with the activity. Used when
 | 
			
		||||
        opening an external file or resource in the
 | 
			
		||||
        activity, rather than a journal object
 | 
			
		||||
        (downloads stored on the file system for
 | 
			
		||||
        example or web pages)
 | 
			
		||||
        
 | 
			
		||||
        invited (bool): True if the activity is being 
 | 
			
		||||
 | 
			
		||||
        invited (bool): True if the activity is being
 | 
			
		||||
        launched for handling an invite from the network
 | 
			
		||||
    '''
 | 
			
		||||
 | 
			
		||||
@ -55,7 +55,7 @@ class ActivityHandle(object):
 | 
			
		||||
        self.invited = invited
 | 
			
		||||
 | 
			
		||||
    def get_dict(self):
 | 
			
		||||
        '''Returns activity settings as a dictionary in format 
 | 
			
		||||
        '''Returns activity settings as a dictionary in format
 | 
			
		||||
            {activity_id:XXXX, object_id:XXXX, uri:XXXX, invited:BOOL}'''
 | 
			
		||||
        result = {'activity_id': self.activity_id, 'invited': self.invited}
 | 
			
		||||
        if self.object_id:
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										222
									
								
								src/sugar3/activity/activityinstance.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										222
									
								
								src/sugar3/activity/activityinstance.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,222 @@
 | 
			
		||||
# Copyright (C) 2006-2008, 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
 | 
			
		||||
 | 
			
		||||
import os
 | 
			
		||||
import sys
 | 
			
		||||
import six
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
# Change the default encoding to avoid UnicodeDecodeError
 | 
			
		||||
# http://lists.sugarlabs.org/archive/sugar-devel/2012-August/038928.html
 | 
			
		||||
if six.PY2:
 | 
			
		||||
    reload(sys)
 | 
			
		||||
    sys.setdefaultencoding('utf-8')
 | 
			
		||||
 | 
			
		||||
import gettext
 | 
			
		||||
from optparse import OptionParser
 | 
			
		||||
 | 
			
		||||
import dbus
 | 
			
		||||
import dbus.service
 | 
			
		||||
from dbus.mainloop.glib import DBusGMainLoop
 | 
			
		||||
DBusGMainLoop(set_as_default=True)
 | 
			
		||||
 | 
			
		||||
from sugar3.activity import activityhandle
 | 
			
		||||
from sugar3 import config
 | 
			
		||||
from sugar3.bundle.activitybundle import ActivityBundle
 | 
			
		||||
from sugar3 import logger
 | 
			
		||||
 | 
			
		||||
from sugar3.bundle.bundle import MalformedBundleException
 | 
			
		||||
 | 
			
		||||
from distutils.dir_util import mkpath
 | 
			
		||||
import time
 | 
			
		||||
import hashlib
 | 
			
		||||
import random
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def create_activity_instance(constructor, handle):
 | 
			
		||||
    activity = constructor(handle)
 | 
			
		||||
    activity.show()
 | 
			
		||||
    return activity
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_single_process_name(bundle_id):
 | 
			
		||||
    return bundle_id
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_single_process_path(bundle_id):
 | 
			
		||||
    return '/' + bundle_id.replace('.', '/')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SingleProcess(dbus.service.Object):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, name_service, constructor):
 | 
			
		||||
        self.constructor = constructor
 | 
			
		||||
 | 
			
		||||
        bus = dbus.SessionBus()
 | 
			
		||||
        bus_name = dbus.service.BusName(name_service, bus=bus)
 | 
			
		||||
        object_path = get_single_process_path(name_service)
 | 
			
		||||
        dbus.service.Object.__init__(self, bus_name, object_path)
 | 
			
		||||
 | 
			
		||||
    @dbus.service.method('org.laptop.SingleProcess', in_signature='a{sv}')
 | 
			
		||||
    def create(self, handle_dict):
 | 
			
		||||
        handle = activityhandle.create_from_dict(handle_dict)
 | 
			
		||||
        create_activity_instance(self.constructor, handle)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def main():
 | 
			
		||||
    usage = 'usage: %prog [options] [activity dir] [python class]'
 | 
			
		||||
    epilog = 'If you are running from a directory containing an Activity, ' \
 | 
			
		||||
             'the argument may be omitted.  Otherwise please provide either '\
 | 
			
		||||
             'a directory containing a Sugar Activity [activity dir], a '\
 | 
			
		||||
             '[python_class], or both.'
 | 
			
		||||
 | 
			
		||||
    parser = OptionParser(usage=usage, epilog=epilog)
 | 
			
		||||
    parser.add_option('-b', '--bundle-id', dest='bundle_id',
 | 
			
		||||
                      help='identifier of the activity bundle')
 | 
			
		||||
    parser.add_option('-a', '--activity-id', dest='activity_id',
 | 
			
		||||
                      help='identifier of the activity instance')
 | 
			
		||||
    parser.add_option('-o', '--object-id', dest='object_id',
 | 
			
		||||
                      help='identifier of the associated datastore object')
 | 
			
		||||
    parser.add_option('-u', '--uri', dest='uri',
 | 
			
		||||
                      help='URI to load')
 | 
			
		||||
    parser.add_option('-s', '--single-process', dest='single_process',
 | 
			
		||||
                      action='store_true',
 | 
			
		||||
                      help='start all the instances in the same process')
 | 
			
		||||
    parser.add_option('-i', '--invited', dest='invited',
 | 
			
		||||
                      action='store_true', default=False,
 | 
			
		||||
                      help='the activity is being launched for handling an '
 | 
			
		||||
                           'invite from the network')
 | 
			
		||||
    (options, args) = parser.parse_args()
 | 
			
		||||
 | 
			
		||||
    logger.start()
 | 
			
		||||
 | 
			
		||||
    activity_class = None
 | 
			
		||||
    if len(args) == 2:
 | 
			
		||||
        activity_class = args[1]
 | 
			
		||||
        os.chdir(args[0])
 | 
			
		||||
    elif len(args) == 1:
 | 
			
		||||
        if os.path.isdir(args[0]):
 | 
			
		||||
            os.chdir(args[0])
 | 
			
		||||
        else:
 | 
			
		||||
            activity_class = args[0]
 | 
			
		||||
 | 
			
		||||
    os.environ['SUGAR_BUNDLE_PATH'] = os.path.abspath(os.curdir)
 | 
			
		||||
 | 
			
		||||
    bundle_path = os.environ['SUGAR_BUNDLE_PATH']
 | 
			
		||||
    sys.path.insert(0, bundle_path)
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        bundle = ActivityBundle(bundle_path)
 | 
			
		||||
    except MalformedBundleException:
 | 
			
		||||
        parser.print_help()
 | 
			
		||||
        exit(0)
 | 
			
		||||
 | 
			
		||||
    if not activity_class:
 | 
			
		||||
        command = bundle.get_command()
 | 
			
		||||
        if command.startswith('sugar-activity'):
 | 
			
		||||
            if not command.startswith('sugar-activity3'):
 | 
			
		||||
                logging.warning("Activity written for Python 2, consider porting to Python 3.")
 | 
			
		||||
            activity_class = command.split(" ")[1]
 | 
			
		||||
 | 
			
		||||
    if 'SUGAR_VERSION' not in os.environ:
 | 
			
		||||
        profile_id = os.environ.get('SUGAR_PROFILE', 'default')
 | 
			
		||||
        home_dir = os.environ.get('SUGAR_HOME', os.path.expanduser('~/.sugar'))
 | 
			
		||||
        base = os.path.join(home_dir, profile_id)
 | 
			
		||||
        activity_root = os.path.join(base, bundle.get_bundle_id())
 | 
			
		||||
 | 
			
		||||
        instance_dir = os.path.join(activity_root, 'instance')
 | 
			
		||||
        mkpath(instance_dir)
 | 
			
		||||
 | 
			
		||||
        data_dir = os.path.join(activity_root, 'data')
 | 
			
		||||
        mkpath(data_dir)
 | 
			
		||||
 | 
			
		||||
        tmp_dir = os.path.join(activity_root, 'tmp')
 | 
			
		||||
        mkpath(tmp_dir)
 | 
			
		||||
 | 
			
		||||
        os.environ['SUGAR_ACTIVITY_ROOT'] = activity_root
 | 
			
		||||
        os.environ['SUGAR_BUNDLE_PATH'] = bundle.get_path()
 | 
			
		||||
 | 
			
		||||
    os.environ['SUGAR_BUNDLE_ID'] = bundle.get_bundle_id()
 | 
			
		||||
    os.environ['SUGAR_BUNDLE_NAME'] = bundle.get_name()
 | 
			
		||||
    os.environ['SUGAR_BUNDLE_VERSION'] = str(bundle.get_activity_version())
 | 
			
		||||
 | 
			
		||||
    # must be done early, some activities set translations globally, SL #3654
 | 
			
		||||
    activity_locale_path = os.environ.get("SUGAR_LOCALEDIR",
 | 
			
		||||
                                          config.locale_path)
 | 
			
		||||
 | 
			
		||||
    gettext.bindtextdomain(bundle.get_bundle_id(), activity_locale_path)
 | 
			
		||||
    gettext.bindtextdomain('sugar-toolkit-gtk3', config.locale_path)
 | 
			
		||||
    gettext.textdomain(bundle.get_bundle_id())
 | 
			
		||||
 | 
			
		||||
    splitted_module = activity_class.rsplit('.', 1)
 | 
			
		||||
    module_name = splitted_module[0]
 | 
			
		||||
    class_name = splitted_module[1]
 | 
			
		||||
 | 
			
		||||
    module = __import__(module_name)
 | 
			
		||||
    for comp in module_name.split('.')[1:]:
 | 
			
		||||
        module = getattr(module, comp)
 | 
			
		||||
 | 
			
		||||
    activity_constructor = getattr(module, class_name)
 | 
			
		||||
 | 
			
		||||
    if not options.activity_id:
 | 
			
		||||
        # Generate random hash
 | 
			
		||||
        data = '%s%s' % (time.time(), random.randint(10000, 100000))
 | 
			
		||||
        random_hash = hashlib.sha1(data.encode()).hexdigest()
 | 
			
		||||
        options.activity_id = random_hash
 | 
			
		||||
        options.bundle_id = bundle.get_bundle_id()
 | 
			
		||||
 | 
			
		||||
    activity_handle = activityhandle.ActivityHandle(
 | 
			
		||||
        activity_id=options.activity_id,
 | 
			
		||||
        object_id=options.object_id, uri=options.uri,
 | 
			
		||||
        invited=options.invited)
 | 
			
		||||
 | 
			
		||||
    if options.single_process is True:
 | 
			
		||||
        sessionbus = dbus.SessionBus()
 | 
			
		||||
 | 
			
		||||
        service_name = get_single_process_name(options.bundle_id)
 | 
			
		||||
        service_path = get_single_process_path(options.bundle_id)
 | 
			
		||||
 | 
			
		||||
        bus_object = sessionbus.get_object(
 | 
			
		||||
            'org.freedesktop.DBus', '/org/freedesktop/DBus')
 | 
			
		||||
        try:
 | 
			
		||||
            name = bus_object.GetNameOwner(
 | 
			
		||||
                service_name, dbus_interface='org.freedesktop.DBus')
 | 
			
		||||
        except dbus.DBusException:
 | 
			
		||||
            name = None
 | 
			
		||||
 | 
			
		||||
        if not name:
 | 
			
		||||
            SingleProcess(service_name, activity_constructor)
 | 
			
		||||
        else:
 | 
			
		||||
            try:
 | 
			
		||||
                single_process = sessionbus.get_object(service_name,
 | 
			
		||||
                                                       service_path)
 | 
			
		||||
                single_process.create(
 | 
			
		||||
                    activity_handle.get_dict(),
 | 
			
		||||
                    dbus_interface='org.laptop.SingleProcess')
 | 
			
		||||
 | 
			
		||||
                print('Created %s in a single process.' % service_name)
 | 
			
		||||
                sys.exit(0)
 | 
			
		||||
            except (TypeError, dbus.DBusException):
 | 
			
		||||
                print('Could not communicate with the instance process,'
 | 
			
		||||
                      'launching a new process')
 | 
			
		||||
 | 
			
		||||
    if hasattr(module, 'start'):
 | 
			
		||||
        module.start()
 | 
			
		||||
 | 
			
		||||
    instance = create_activity_instance(activity_constructor, activity_handle)
 | 
			
		||||
 | 
			
		||||
    if hasattr(instance, 'run_main_loop'):
 | 
			
		||||
        instance.run_main_loop()
 | 
			
		||||
@ -79,5 +79,5 @@ class ActivityService(dbus.service.Object):
 | 
			
		||||
    def GetDocumentPath(self, async_cb, async_err_cb):
 | 
			
		||||
        try:
 | 
			
		||||
            self._activity.get_document_path(async_cb, async_err_cb)
 | 
			
		||||
        except Exception, e:
 | 
			
		||||
        except Exception as e:
 | 
			
		||||
            async_err_cb(e)
 | 
			
		||||
 | 
			
		||||
@ -40,12 +40,13 @@ import gettext
 | 
			
		||||
import logging
 | 
			
		||||
from glob import glob
 | 
			
		||||
from fnmatch import fnmatch
 | 
			
		||||
from ConfigParser import ConfigParser
 | 
			
		||||
from six.moves.configparser import ConfigParser
 | 
			
		||||
import xml.etree.cElementTree as ET
 | 
			
		||||
from HTMLParser import HTMLParser
 | 
			
		||||
from six.moves.html_parser import HTMLParser
 | 
			
		||||
 | 
			
		||||
from sugar3 import env
 | 
			
		||||
from sugar3.bundle.activitybundle import ActivityBundle
 | 
			
		||||
from six.moves import reduce
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
IGNORE_DIRS = ['dist', '.git', 'screenshots']
 | 
			
		||||
@ -150,7 +151,7 @@ class Builder(object):
 | 
			
		||||
            args = ['msgfmt', '--output-file=%s' % mo_file, file_name]
 | 
			
		||||
            retcode = subprocess.call(args)
 | 
			
		||||
            if retcode:
 | 
			
		||||
                print 'ERROR - msgfmt failed with return code %i.' % retcode
 | 
			
		||||
                print('ERROR - msgfmt failed with return code %i.' % retcode)
 | 
			
		||||
                if self._no_fail:
 | 
			
		||||
                    continue
 | 
			
		||||
 | 
			
		||||
@ -301,8 +302,8 @@ class Installer(Packager):
 | 
			
		||||
 | 
			
		||||
            source_to_dest[source_path] = dest_path
 | 
			
		||||
 | 
			
		||||
        for source, dest in source_to_dest.items():
 | 
			
		||||
            print 'Install %s to %s.' % (source, dest)
 | 
			
		||||
        for source, dest in list(source_to_dest.items()):
 | 
			
		||||
            print('Install %s to %s.' % (source, dest))
 | 
			
		||||
 | 
			
		||||
            path = os.path.dirname(dest)
 | 
			
		||||
            if not os.path.exists(path):
 | 
			
		||||
@ -429,7 +430,7 @@ def cmd_check(config, options):
 | 
			
		||||
    if options.choice == 'integration':
 | 
			
		||||
        run_unit_test = False
 | 
			
		||||
 | 
			
		||||
    print "Running Tests"
 | 
			
		||||
    print("Running Tests")
 | 
			
		||||
 | 
			
		||||
    test_path = os.path.join(config.source_dir, "tests")
 | 
			
		||||
 | 
			
		||||
@ -443,22 +444,22 @@ def cmd_check(config, options):
 | 
			
		||||
            all_tests = unittest.defaultTestLoader.discover(unit_test_path)
 | 
			
		||||
            unittest.TextTestRunner(verbosity=options.verbose).run(all_tests)
 | 
			
		||||
        elif not run_unit_test:
 | 
			
		||||
            print "Not running unit tests"
 | 
			
		||||
            print("Not running unit tests")
 | 
			
		||||
        else:
 | 
			
		||||
            print 'No "unit" directory found.'
 | 
			
		||||
            print('No "unit" directory found.')
 | 
			
		||||
 | 
			
		||||
        if os.path.isdir(integration_test_path) and run_integration_test:
 | 
			
		||||
            all_tests = unittest.defaultTestLoader.discover(
 | 
			
		||||
                integration_test_path)
 | 
			
		||||
            unittest.TextTestRunner(verbosity=options.verbose).run(all_tests)
 | 
			
		||||
        elif not run_integration_test:
 | 
			
		||||
            print "Not running integration tests"
 | 
			
		||||
            print("Not running integration tests")
 | 
			
		||||
        else:
 | 
			
		||||
            print 'No "integration" directory found.'
 | 
			
		||||
            print('No "integration" directory found.')
 | 
			
		||||
 | 
			
		||||
        print "Finished testing"
 | 
			
		||||
        print("Finished testing")
 | 
			
		||||
    else:
 | 
			
		||||
        print "Error: No tests/ directory"
 | 
			
		||||
        print("Error: No tests/ directory")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def cmd_dev(config, options):
 | 
			
		||||
@ -472,9 +473,9 @@ def cmd_dev(config, options):
 | 
			
		||||
        os.symlink(config.source_dir, bundle_path)
 | 
			
		||||
    except OSError:
 | 
			
		||||
        if os.path.islink(bundle_path):
 | 
			
		||||
            print 'ERROR - The bundle has been already setup for development.'
 | 
			
		||||
            print('ERROR - The bundle has been already setup for development.')
 | 
			
		||||
        else:
 | 
			
		||||
            print 'ERROR - A bundle with the same name is already installed.'
 | 
			
		||||
            print('ERROR - A bundle with the same name is already installed.')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def cmd_dist_xo(config, options):
 | 
			
		||||
@ -490,9 +491,9 @@ def cmd_dist_xo(config, options):
 | 
			
		||||
def cmd_fix_manifest(config, options):
 | 
			
		||||
    '''Add missing files to the manifest (OBSOLETE)'''
 | 
			
		||||
 | 
			
		||||
    print 'WARNING: The fix_manifest command is obsolete.'
 | 
			
		||||
    print '         The MANIFEST file is no longer used in bundles,'
 | 
			
		||||
    print '         please remove it.'
 | 
			
		||||
    print('WARNING: The fix_manifest command is obsolete.')
 | 
			
		||||
    print('         The MANIFEST file is no longer used in bundles,')
 | 
			
		||||
    print('         please remove it.')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def cmd_dist_source(config, options):
 | 
			
		||||
@ -506,7 +507,10 @@ def cmd_install(config, options):
 | 
			
		||||
    """Install the activity in the system"""
 | 
			
		||||
 | 
			
		||||
    installer = Installer(Builder(config))
 | 
			
		||||
    installer.install(options.prefix, options.install_mime, options.install_desktop_file)
 | 
			
		||||
    installer.install(
 | 
			
		||||
        options.prefix,
 | 
			
		||||
        options.install_mime,
 | 
			
		||||
        options.install_desktop_file)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _po_escape(string):
 | 
			
		||||
@ -568,7 +572,7 @@ def cmd_genpot(config, options):
 | 
			
		||||
    args += python_files
 | 
			
		||||
    retcode = subprocess.call(args)
 | 
			
		||||
    if retcode:
 | 
			
		||||
        print 'ERROR - xgettext failed with return code %i.' % retcode
 | 
			
		||||
        print('ERROR - xgettext failed with return code %i.' % retcode)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def cmd_build(config, options):
 | 
			
		||||
@ -603,7 +607,7 @@ def start():
 | 
			
		||||
                              choices=['unit', 'integration'],
 | 
			
		||||
                              help="run unit/integration test")
 | 
			
		||||
    check_parser.add_argument("--verbosity", "-v", dest="verbose",
 | 
			
		||||
                              type=int, choices=range(0, 3),
 | 
			
		||||
                              type=int, choices=list(range(0, 3)),
 | 
			
		||||
                              default=1, nargs='?',
 | 
			
		||||
                              help="verbosity for the unit tests")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -28,8 +28,8 @@ GObject.threads_init()
 | 
			
		||||
from gi.repository import WebKit
 | 
			
		||||
import socket
 | 
			
		||||
from threading import Thread
 | 
			
		||||
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
 | 
			
		||||
import SocketServer
 | 
			
		||||
from six.moves.BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
 | 
			
		||||
from six.moves import socketserver
 | 
			
		||||
import select
 | 
			
		||||
import errno
 | 
			
		||||
import mimetypes
 | 
			
		||||
@ -80,7 +80,7 @@ class LocalHTTPServer(HTTPServer):
 | 
			
		||||
            # shutdown request and wastes cpu at all other times.
 | 
			
		||||
            try:
 | 
			
		||||
                r, w, e = select.select([self], [], [], poll_interval)
 | 
			
		||||
            except select.error, e:
 | 
			
		||||
            except select.error as e:
 | 
			
		||||
                if e[0] == errno.EINTR:
 | 
			
		||||
                    logging.debug("got eintr")
 | 
			
		||||
                    continue
 | 
			
		||||
@ -92,7 +92,7 @@ class LocalHTTPServer(HTTPServer):
 | 
			
		||||
    def server_bind(self):
 | 
			
		||||
        """Override server_bind in HTTPServer to not use
 | 
			
		||||
        getfqdn to get the server name because is very slow."""
 | 
			
		||||
        SocketServer.TCPServer.server_bind(self)
 | 
			
		||||
        socketserver.TCPServer.server_bind(self)
 | 
			
		||||
        _host, port = self.socket.getsockname()[:2]
 | 
			
		||||
        self.server_name = 'localhost'
 | 
			
		||||
        self.server_port = port
 | 
			
		||||
 | 
			
		||||
@ -34,7 +34,7 @@ from sugar3.graphics.palettemenu import PaletteMenuBox
 | 
			
		||||
from sugar3 import profile
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
_ = lambda msg: gettext.dgettext('sugar-toolkit-gtk3', msg)
 | 
			
		||||
def _(msg): return gettext.dgettext('sugar-toolkit-gtk3', msg)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _create_activity_icon(metadata):
 | 
			
		||||
 | 
			
		||||
@ -44,7 +44,9 @@ an `[Activity]` header on the first line:
 | 
			
		||||
* `icon` - the icon file for the activity, shown by Sugar in the list
 | 
			
		||||
  of installed activities,
 | 
			
		||||
 | 
			
		||||
* `exec` - how to execute the activity, e.g. `sugar-activity module.Class`,
 | 
			
		||||
* `exec` - how to execute the activity, e.g.
 | 
			
		||||
  `sugar-activity3 module.Class` (For activities written for Python 3),
 | 
			
		||||
  `sugar-activity module.Class` (For activities written for Python 2)
 | 
			
		||||
 | 
			
		||||
Optional metadata keys are;
 | 
			
		||||
 | 
			
		||||
@ -117,7 +119,7 @@ Example `activity.info`
 | 
			
		||||
    [Activity]
 | 
			
		||||
    name = Browse
 | 
			
		||||
    bundle_id = org.laptop.WebActivity
 | 
			
		||||
    exec = sugar-activity webactivity.WebActivity -s
 | 
			
		||||
    exec = sugar-activity3 webactivity.WebActivity -s
 | 
			
		||||
    activity_version = 200
 | 
			
		||||
    icon = activity-web
 | 
			
		||||
    max_participants = 100
 | 
			
		||||
 | 
			
		||||
@ -20,7 +20,7 @@
 | 
			
		||||
UNSTABLE.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from ConfigParser import ConfigParser, ParsingError
 | 
			
		||||
from six.moves.configparser import ConfigParser, ParsingError
 | 
			
		||||
from locale import normalize
 | 
			
		||||
import os
 | 
			
		||||
import shutil
 | 
			
		||||
@ -441,7 +441,7 @@ class ActivityBundle(Bundle):
 | 
			
		||||
        if delete_profile:
 | 
			
		||||
            bundle_profile_path = env.get_profile_path(self._bundle_id)
 | 
			
		||||
            if os.path.exists(bundle_profile_path):
 | 
			
		||||
                os.chmod(bundle_profile_path, 0775)
 | 
			
		||||
                os.chmod(bundle_profile_path, 0o775)
 | 
			
		||||
                shutil.rmtree(bundle_profile_path, ignore_errors=True)
 | 
			
		||||
 | 
			
		||||
        self._uninstall(install_path)
 | 
			
		||||
 | 
			
		||||
@ -20,10 +20,10 @@
 | 
			
		||||
UNSTABLE.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import six
 | 
			
		||||
import os
 | 
			
		||||
import logging
 | 
			
		||||
import shutil
 | 
			
		||||
import StringIO
 | 
			
		||||
import zipfile
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -74,7 +74,7 @@ class Bundle(object):
 | 
			
		||||
        if not os.path.isdir(self._path):
 | 
			
		||||
            try:
 | 
			
		||||
                self._zip_file = zipfile.ZipFile(self._path)
 | 
			
		||||
            except zipfile.error, exception:
 | 
			
		||||
            except zipfile.error as exception:
 | 
			
		||||
                raise MalformedBundleException('Error accessing zip file %r: '
 | 
			
		||||
                                               '%s' % (self._path, exception))
 | 
			
		||||
            self._check_zip_bundle()
 | 
			
		||||
@ -115,7 +115,7 @@ class Bundle(object):
 | 
			
		||||
        if self._zip_file is None:
 | 
			
		||||
            path = os.path.join(self._path, filename)
 | 
			
		||||
            try:
 | 
			
		||||
                f = open(path, 'rb')
 | 
			
		||||
                f = open(path, 'r')
 | 
			
		||||
            except IOError:
 | 
			
		||||
                logging.debug("cannot open path %s" % path)
 | 
			
		||||
                return None
 | 
			
		||||
@ -123,7 +123,7 @@ class Bundle(object):
 | 
			
		||||
            path = os.path.join(self._zip_root_dir, filename)
 | 
			
		||||
            try:
 | 
			
		||||
                data = self._zip_file.read(path)
 | 
			
		||||
                f = StringIO.StringIO(data)
 | 
			
		||||
                f = six.StringIO(data)
 | 
			
		||||
            except KeyError:
 | 
			
		||||
                logging.debug('%s not found in zip %s.' % (filename, path))
 | 
			
		||||
                return None
 | 
			
		||||
@ -171,7 +171,7 @@ class Bundle(object):
 | 
			
		||||
            raise AlreadyInstalledException
 | 
			
		||||
 | 
			
		||||
        if not os.path.isdir(install_dir):
 | 
			
		||||
            os.mkdir(install_dir, 0775)
 | 
			
		||||
            os.mkdir(install_dir, 0o775)
 | 
			
		||||
 | 
			
		||||
        # zipfile provides API that in theory would let us do this
 | 
			
		||||
        # correctly by hand, but handling all the oddities of
 | 
			
		||||
 | 
			
		||||
@ -82,6 +82,7 @@ class NormalizedVersion(object):
 | 
			
		||||
    Attributes:
 | 
			
		||||
        parts (list): the numeric parts of the version after normalization.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, activity_version):
 | 
			
		||||
        self._activity_version = activity_version
 | 
			
		||||
        self.parts = []
 | 
			
		||||
 | 
			
		||||
@ -21,10 +21,11 @@
 | 
			
		||||
UNSTABLE.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from ConfigParser import ConfigParser
 | 
			
		||||
from six.moves import urllib
 | 
			
		||||
from six.moves.configparser import ConfigParser
 | 
			
		||||
 | 
			
		||||
import tempfile
 | 
			
		||||
import os
 | 
			
		||||
import urllib
 | 
			
		||||
 | 
			
		||||
from sugar3 import env
 | 
			
		||||
from sugar3.bundle.bundle import Bundle, MalformedBundleException
 | 
			
		||||
@ -142,7 +143,7 @@ class ContentBundle(Bundle):
 | 
			
		||||
 | 
			
		||||
    def get_start_uri(self):
 | 
			
		||||
        path = os.path.join(self.get_path(), self._activity_start)
 | 
			
		||||
        return 'file://' + urllib.pathname2url(path)
 | 
			
		||||
        return 'file://' + urllib.request.pathname2url(path)
 | 
			
		||||
 | 
			
		||||
    def get_bundle_id(self):
 | 
			
		||||
        return self._global_name
 | 
			
		||||
 | 
			
		||||
@ -20,6 +20,7 @@
 | 
			
		||||
STABLE
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import six
 | 
			
		||||
import logging
 | 
			
		||||
import time
 | 
			
		||||
from datetime import datetime
 | 
			
		||||
@ -69,6 +70,7 @@ def __datastore_updated_cb(object_id):
 | 
			
		||||
def __datastore_deleted_cb(object_id):
 | 
			
		||||
    deleted.send(None, object_id=object_id)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
created = dispatch.Signal()
 | 
			
		||||
deleted = dispatch.Signal()
 | 
			
		||||
updated = dispatch.Signal()
 | 
			
		||||
@ -85,6 +87,12 @@ class DSMetadata(GObject.GObject):
 | 
			
		||||
        if not properties:
 | 
			
		||||
            self._properties = {}
 | 
			
		||||
        else:
 | 
			
		||||
            if six.PY3:
 | 
			
		||||
                for x, y in properties.items():
 | 
			
		||||
                    try:
 | 
			
		||||
                        properties[x] = y.decode()
 | 
			
		||||
                    except BaseException:
 | 
			
		||||
                        pass
 | 
			
		||||
            self._properties = properties
 | 
			
		||||
 | 
			
		||||
        default_keys = ['activity', 'activity_id',
 | 
			
		||||
@ -97,6 +105,11 @@ class DSMetadata(GObject.GObject):
 | 
			
		||||
        return self._properties[key]
 | 
			
		||||
 | 
			
		||||
    def __setitem__(self, key, value):
 | 
			
		||||
        if six.PY3:
 | 
			
		||||
            try:
 | 
			
		||||
                value = value.decode()
 | 
			
		||||
            except BaseException:
 | 
			
		||||
                pass
 | 
			
		||||
        if key not in self._properties or self._properties[key] != value:
 | 
			
		||||
            self._properties[key] = value
 | 
			
		||||
            self.emit('updated')
 | 
			
		||||
@ -112,7 +125,7 @@ class DSMetadata(GObject.GObject):
 | 
			
		||||
        return key in self._properties
 | 
			
		||||
 | 
			
		||||
    def keys(self):
 | 
			
		||||
        return self._properties.keys()
 | 
			
		||||
        return list(self._properties.keys())
 | 
			
		||||
 | 
			
		||||
    def get_dictionary(self):
 | 
			
		||||
        return self._properties
 | 
			
		||||
@ -128,7 +141,7 @@ class DSMetadata(GObject.GObject):
 | 
			
		||||
 | 
			
		||||
    def update(self, properties):
 | 
			
		||||
        """Update all of the metadata"""
 | 
			
		||||
        for (key, value) in properties.items():
 | 
			
		||||
        for (key, value) in list(properties.items()):
 | 
			
		||||
            self[key] = value
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,6 @@
 | 
			
		||||
import weakref
 | 
			
		||||
import six
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    set
 | 
			
		||||
except NameError:
 | 
			
		||||
@ -11,7 +13,7 @@ WEAKREF_TYPES = (weakref.ReferenceType, saferef.BoundMethodWeakref)
 | 
			
		||||
 | 
			
		||||
def _make_id(target):
 | 
			
		||||
    if hasattr(target, 'im_func'):
 | 
			
		||||
        return (id(target.im_self), id(target.im_func))
 | 
			
		||||
        return (id(im_self(target)), id(im_func(target)))
 | 
			
		||||
    return id(target)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -159,7 +161,7 @@ class Signal(object):
 | 
			
		||||
        for receiver in self._live_receivers(_make_id(sender)):
 | 
			
		||||
            try:
 | 
			
		||||
                response = receiver(signal=self, sender=sender, **named)
 | 
			
		||||
            except Exception, err:
 | 
			
		||||
            except Exception as err:
 | 
			
		||||
                responses.append((receiver, err))
 | 
			
		||||
            else:
 | 
			
		||||
                responses.append((receiver, response))
 | 
			
		||||
@ -195,3 +197,17 @@ class Signal(object):
 | 
			
		||||
            for idx, (r_key, _) in enumerate(self.receivers):
 | 
			
		||||
                if r_key == key:
 | 
			
		||||
                    del self.receivers[idx]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def im_self(func):
 | 
			
		||||
    if six.PY2:
 | 
			
		||||
        return func.im_self
 | 
			
		||||
    elif six.PY3:
 | 
			
		||||
        return func.__self__
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def im_func(func):
 | 
			
		||||
    if six.PY2:
 | 
			
		||||
        return func.im_func
 | 
			
		||||
    elif six.PY3:
 | 
			
		||||
        return func.__func__
 | 
			
		||||
 | 
			
		||||
@ -5,6 +5,7 @@ Provides a way to safely weakref any function, including bound methods (which
 | 
			
		||||
aren't handled by the core weakref module).
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import six
 | 
			
		||||
import weakref
 | 
			
		||||
import traceback
 | 
			
		||||
 | 
			
		||||
@ -21,7 +22,7 @@ def safeRef(target, onDelete=None):
 | 
			
		||||
        weakref or a BoundMethodWeakref) as argument.
 | 
			
		||||
    """
 | 
			
		||||
    if hasattr(target, 'im_self'):
 | 
			
		||||
        if target.im_self is not None:
 | 
			
		||||
        if im_self(target) is not None:
 | 
			
		||||
            # Turn a bound method into a BoundMethodWeakref instance.
 | 
			
		||||
            # Keep track of these instances for lookup by disconnect().
 | 
			
		||||
            assert hasattr(target, 'im_func'), \
 | 
			
		||||
@ -123,18 +124,18 @@ class BoundMethodWeakref(object):
 | 
			
		||||
                try:
 | 
			
		||||
                    if callable(function):
 | 
			
		||||
                        function(self)
 | 
			
		||||
                except Exception, e:
 | 
			
		||||
                except Exception as e:
 | 
			
		||||
                    try:
 | 
			
		||||
                        traceback.print_exc()
 | 
			
		||||
                    except AttributeError:
 | 
			
		||||
                        print "Exception during saferef %s cleanup "
 | 
			
		||||
                        "function %s: %s" % (self, function, e)
 | 
			
		||||
                        print("Exception during saferef %s cleanup "
 | 
			
		||||
                              "function %s: %s" % (self, function, e))
 | 
			
		||||
        self.deletionMethods = [onDelete]
 | 
			
		||||
        self.key = self.calculateKey(target)
 | 
			
		||||
        self.weakSelf = weakref.ref(target.im_self, remove)
 | 
			
		||||
        self.weakFunc = weakref.ref(target.im_func, remove)
 | 
			
		||||
        self.selfName = str(target.im_self)
 | 
			
		||||
        self.funcName = str(target.im_func.__name__)
 | 
			
		||||
        self.weakSelf = weakref.ref(im_self(target), remove)
 | 
			
		||||
        self.weakFunc = weakref.ref(im_func(target), remove)
 | 
			
		||||
        self.selfName = str(im_self(target))
 | 
			
		||||
        self.funcName = str(im_func(target).__name__)
 | 
			
		||||
 | 
			
		||||
    def calculateKey(cls, target):
 | 
			
		||||
        """Calculate the reference key for this reference
 | 
			
		||||
@ -142,7 +143,7 @@ class BoundMethodWeakref(object):
 | 
			
		||||
        Currently this is a two-tuple of the id()'s of the
 | 
			
		||||
        target object and the target function respectively.
 | 
			
		||||
        """
 | 
			
		||||
        return (id(target.im_self), id(target.im_func))
 | 
			
		||||
        return (id(im_self(target)), id(im_func(target)))
 | 
			
		||||
    calculateKey = classmethod(calculateKey)
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
@ -155,15 +156,19 @@ class BoundMethodWeakref(object):
 | 
			
		||||
 | 
			
		||||
    __repr__ = __str__
 | 
			
		||||
 | 
			
		||||
    def __nonzero__(self):
 | 
			
		||||
    def __bool__(self):
 | 
			
		||||
        """Whether we are still a valid reference"""
 | 
			
		||||
        return self() is not None
 | 
			
		||||
 | 
			
		||||
    def __nonzero__(self):
 | 
			
		||||
        """Python2 alternative for __bool__"""
 | 
			
		||||
        return self() is not None
 | 
			
		||||
 | 
			
		||||
    def __cmp__(self, other):
 | 
			
		||||
        """Compare with another reference"""
 | 
			
		||||
        if not isinstance(other, self.__class__):
 | 
			
		||||
            return cmp(self.__class__, type(other))
 | 
			
		||||
        return cmp(self.key, other.key)
 | 
			
		||||
            return ((self.__class__ > type(other)) - (self.__class__ < type(other)))
 | 
			
		||||
        return ((self.key > other.key) - (self.key < other.key))
 | 
			
		||||
 | 
			
		||||
    def __call__(self):
 | 
			
		||||
        """Return a strong reference to the bound method
 | 
			
		||||
@ -201,6 +206,7 @@ class BoundNonDescriptorMethodWeakref(BoundMethodWeakref):
 | 
			
		||||
    aren't descriptors (such as Jython) this implementation has the advantage
 | 
			
		||||
    of working in the most cases.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, target, onDelete=None):
 | 
			
		||||
        """Return a weak-reference-like instance for a bound method
 | 
			
		||||
 | 
			
		||||
@ -215,9 +221,9 @@ class BoundNonDescriptorMethodWeakref(BoundMethodWeakref):
 | 
			
		||||
            collected).  Should take a single argument,
 | 
			
		||||
            which will be passed a pointer to this object.
 | 
			
		||||
        """
 | 
			
		||||
        assert getattr(target.im_self, target.__name__) == target, \
 | 
			
		||||
        assert getattr(im_self(target), target.__name__) == target, \
 | 
			
		||||
            ("method %s isn't available as the attribute %s of %s" %
 | 
			
		||||
                (target, target.__name__, target.im_self))
 | 
			
		||||
                (target, target.__name__, im_self(target)))
 | 
			
		||||
        super(BoundNonDescriptorMethodWeakref, self).__init__(target, onDelete)
 | 
			
		||||
 | 
			
		||||
    def __call__(self):
 | 
			
		||||
@ -255,3 +261,17 @@ def get_bound_method_weakref(target, onDelete):
 | 
			
		||||
        # no luck, use the alternative implementation:
 | 
			
		||||
        return BoundNonDescriptorMethodWeakref(target=target,
 | 
			
		||||
                                               onDelete=onDelete)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def im_self(func):
 | 
			
		||||
    if six.PY2:
 | 
			
		||||
        return func.im_self
 | 
			
		||||
    elif six.PY3:
 | 
			
		||||
        return func.__self__
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def im_func(func):
 | 
			
		||||
    if six.PY2:
 | 
			
		||||
        return func.im_func
 | 
			
		||||
    elif six.PY3:
 | 
			
		||||
        return func.__func__
 | 
			
		||||
 | 
			
		||||
@ -36,9 +36,9 @@ def get_profile_path(path=None):
 | 
			
		||||
    base = os.path.join(home_dir, profile_id)
 | 
			
		||||
    if not os.path.isdir(base):
 | 
			
		||||
        try:
 | 
			
		||||
            os.makedirs(base, 0770)
 | 
			
		||||
            os.makedirs(base, 0o770)
 | 
			
		||||
        except OSError:
 | 
			
		||||
            print 'Could not create user directory.'
 | 
			
		||||
            print('Could not create user directory.')
 | 
			
		||||
 | 
			
		||||
    if path is not None:
 | 
			
		||||
        return os.path.join(base, path)
 | 
			
		||||
 | 
			
		||||
@ -16,7 +16,6 @@ sugar_PYTHON =              \
 | 
			
		||||
	palettemenu.py          \
 | 
			
		||||
	palettewindow.py        \
 | 
			
		||||
	panel.py                \
 | 
			
		||||
	popwindow.py            \
 | 
			
		||||
	radiopalette.py         \
 | 
			
		||||
	radiotoolbutton.py      \
 | 
			
		||||
	scrollingdetector.py    \
 | 
			
		||||
 | 
			
		||||
@ -61,7 +61,7 @@ from sugar3.graphics import style
 | 
			
		||||
from sugar3.graphics.icon import Icon
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
_ = lambda msg: gettext.dgettext('sugar-toolkit-gtk3', msg)
 | 
			
		||||
def _(msg): return gettext.dgettext('sugar-toolkit-gtk3', msg)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if not hasattr(GObject.ParamFlags, 'READWRITE'):
 | 
			
		||||
@ -258,6 +258,8 @@ class Alert(Gtk.EventBox):
 | 
			
		||||
 | 
			
		||||
    def __button_clicked_cb(self, button, response_id):
 | 
			
		||||
        self._response(response_id)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if hasattr(Alert, 'set_css_name'):
 | 
			
		||||
    Alert.set_css_name('alert')
 | 
			
		||||
 | 
			
		||||
@ -294,9 +296,9 @@ class ConfirmationAlert(Alert):
 | 
			
		||||
 | 
			
		||||
            # Check the response identifier.
 | 
			
		||||
            if response_id is Gtk.ResponseType.OK:
 | 
			
		||||
                print 'Ok Button was clicked.'
 | 
			
		||||
                print('Ok Button was clicked.')
 | 
			
		||||
            elif response_id is Gtk.ResponseType.CANCEL:
 | 
			
		||||
                print 'Cancel Button was clicked.'
 | 
			
		||||
                print('Cancel Button was clicked.')
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, **kwargs):
 | 
			
		||||
@ -341,7 +343,7 @@ class ErrorAlert(Alert):
 | 
			
		||||
 | 
			
		||||
            # Check the response identifier.
 | 
			
		||||
            if response_id is Gtk.ResponseType.OK:
 | 
			
		||||
                print 'Ok Button was clicked.'
 | 
			
		||||
                print('Ok Button was clicked.')
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, **kwargs):
 | 
			
		||||
@ -390,6 +392,8 @@ class _TimeoutIcon(Gtk.Alignment):
 | 
			
		||||
 | 
			
		||||
    def set_text(self, text):
 | 
			
		||||
        self._text.set_markup('<b>%s</b>' % GLib.markup_escape_text(str(text)))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if hasattr(_TimeoutIcon, 'set_css_name'):
 | 
			
		||||
    _TimeoutIcon.set_css_name('timeouticon')
 | 
			
		||||
 | 
			
		||||
@ -458,11 +462,11 @@ class TimeoutAlert(_TimeoutAlert):
 | 
			
		||||
 | 
			
		||||
            # Check the response identifier.
 | 
			
		||||
            if response_id is Gtk.ResponseType.OK:
 | 
			
		||||
                print 'Continue Button was clicked.'
 | 
			
		||||
                print('Continue Button was clicked.')
 | 
			
		||||
            elif response_id is Gtk.ResponseType.CANCEL:
 | 
			
		||||
                print 'Cancel Button was clicked.'
 | 
			
		||||
                print('Cancel Button was clicked.')
 | 
			
		||||
            elif response_id == -1:
 | 
			
		||||
                print 'Timeout occurred'
 | 
			
		||||
                print('Timeout occurred')
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, timeout=5, **kwargs):
 | 
			
		||||
@ -508,9 +512,9 @@ class NotifyAlert(_TimeoutAlert):
 | 
			
		||||
 | 
			
		||||
            # Check the response identifier.
 | 
			
		||||
            if response_id is Gtk.ResponseType.OK:
 | 
			
		||||
                print 'Ok Button was clicked.'
 | 
			
		||||
                print('Ok Button was clicked.')
 | 
			
		||||
            elif response_id == -1:
 | 
			
		||||
                print 'Timeout occurred'
 | 
			
		||||
                print('Timeout occurred')
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, timeout=5, **kwargs):
 | 
			
		||||
 | 
			
		||||
@ -29,7 +29,7 @@ from sugar3.graphics.icon import Icon
 | 
			
		||||
from sugar3.graphics.palette import Palette, ToolInvoker, WidgetInvoker
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
_ = lambda msg: gettext.dgettext('sugar-toolkit-gtk3', msg)
 | 
			
		||||
def _(msg): return gettext.dgettext('sugar-toolkit-gtk3', msg)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if not hasattr(GObject.ParamFlags, 'READWRITE'):
 | 
			
		||||
@ -38,8 +38,8 @@ if not hasattr(GObject.ParamFlags, 'READWRITE'):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_svg_color_string(color):
 | 
			
		||||
    return '#%.2X%.2X%.2X' % (color.red / 257, color.green / 257,
 | 
			
		||||
                              color.blue / 257)
 | 
			
		||||
    return '#%.2X%.2X%.2X' % (color.red // 257, color.green // 257,
 | 
			
		||||
                              color.blue // 257)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class _ColorButton(Gtk.Button):
 | 
			
		||||
@ -123,8 +123,8 @@ class _ColorButton(Gtk.Button):
 | 
			
		||||
        context = self.get_style_context()
 | 
			
		||||
        fg_color = context.get_color(Gtk.StateType.NORMAL)
 | 
			
		||||
        # the color components are stored as float values between 0.0 and 1.0
 | 
			
		||||
        return '#%.2X%.2X%.2X' % (fg_color.red * 255, fg_color.green * 255,
 | 
			
		||||
                                  fg_color.blue * 255)
 | 
			
		||||
        return '#%.2X%.2X%.2X' % (int(fg_color.red * 255), int(fg_color.green * 255),
 | 
			
		||||
                                  int(fg_color.blue * 255))
 | 
			
		||||
 | 
			
		||||
    def set_color(self, color):
 | 
			
		||||
        assert isinstance(color, Gdk.Color)
 | 
			
		||||
@ -149,11 +149,11 @@ class _ColorButton(Gtk.Button):
 | 
			
		||||
        '''
 | 
			
		||||
        Sets the icon for the tool button from a named themed icon.
 | 
			
		||||
        If it is none then no icon will be shown.
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            icon_name(string): The name for a themed icon.
 | 
			
		||||
            It can be set as 'None' too.
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        Example:
 | 
			
		||||
            set_icon_name('view-radial')
 | 
			
		||||
        '''
 | 
			
		||||
@ -169,11 +169,11 @@ class _ColorButton(Gtk.Button):
 | 
			
		||||
    icon_name = GObject.Property(type=str,
 | 
			
		||||
                                 getter=get_icon_name, setter=set_icon_name)
 | 
			
		||||
 | 
			
		||||
    def set_icon_size(self, icon_size):
 | 
			
		||||
        self._preview.props.icon_size = icon_size
 | 
			
		||||
    def set_icon_size(self, pixel_size):
 | 
			
		||||
        self._preview.props.pixel_size = pixel_size
 | 
			
		||||
 | 
			
		||||
    def get_icon_size(self):
 | 
			
		||||
        return self._preview.props.icon_size
 | 
			
		||||
        return self._preview.props.pixel_size
 | 
			
		||||
 | 
			
		||||
    icon_size = GObject.Property(type=int,
 | 
			
		||||
                                 getter=get_icon_size, setter=set_icon_size)
 | 
			
		||||
@ -496,13 +496,13 @@ class ColorToolButton(Gtk.ToolItem):
 | 
			
		||||
    def set_accelerator(self, accelerator):
 | 
			
		||||
        '''
 | 
			
		||||
        Sets keyboard shortcut that activates this button.
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            accelerator(string): accelerator to be set. Should be in
 | 
			
		||||
            form <modifier>Letter
 | 
			
		||||
            Find about format here :
 | 
			
		||||
            https://developer.gnome.org/gtk3/stable/gtk3-Keyboard-Accelerators.html#gtk-accelerator-parse
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        Example:
 | 
			
		||||
            set_accelerator(self, 'accel')
 | 
			
		||||
        '''
 | 
			
		||||
@ -523,21 +523,21 @@ class ColorToolButton(Gtk.ToolItem):
 | 
			
		||||
        The create_palette function is called when the palette needs to be
 | 
			
		||||
        invoked.  For example, when the user has right clicked the icon or
 | 
			
		||||
        the user has hovered over the icon for a long time.
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        The create_palette will only be called once or zero times.  The palette
 | 
			
		||||
        returned will be stored and re-used if the user invokes the palette
 | 
			
		||||
        multiple times.
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        Your create_palette implementation does not need to
 | 
			
		||||
        :any:`Gtk.Widget.show` the palette, as this will be done by the
 | 
			
		||||
        invoker.  However, you still need to show
 | 
			
		||||
        the menu items, etc that you place in the palette.
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            sugar3.graphics.palette.Palette, or None to indicate that you
 | 
			
		||||
            do not want a palette shown
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        The default implementation returns None, to indicate no palette should
 | 
			
		||||
        be shown.
 | 
			
		||||
        '''
 | 
			
		||||
@ -595,11 +595,11 @@ class ColorToolButton(Gtk.ToolItem):
 | 
			
		||||
        '''
 | 
			
		||||
        Sets the icon for the tool button from a named themed icon.
 | 
			
		||||
        If it is none then no icon will be shown.
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            icon_name(string): The name for a themed icon.
 | 
			
		||||
            It can be set as 'None' too.
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        Example:
 | 
			
		||||
            set_icon_name('view-radial')
 | 
			
		||||
        '''
 | 
			
		||||
@ -632,8 +632,8 @@ class ColorToolButton(Gtk.ToolItem):
 | 
			
		||||
 | 
			
		||||
    def set_title(self, title):
 | 
			
		||||
        '''
 | 
			
		||||
        The set_title() method sets the "title" property to the value of 
 | 
			
		||||
        title. The "title" property contains the string that is used to 
 | 
			
		||||
        The set_title() method sets the "title" property to the value of
 | 
			
		||||
        title. The "title" property contains the string that is used to
 | 
			
		||||
        set the colorbutton title.
 | 
			
		||||
        '''
 | 
			
		||||
        self.get_child().props.title = title
 | 
			
		||||
 | 
			
		||||
@ -87,11 +87,13 @@ In this example, the badge will be centered at 97.0% on the X axis,
 | 
			
		||||
and 85.0% on the Y axis.
 | 
			
		||||
'''
 | 
			
		||||
 | 
			
		||||
import six
 | 
			
		||||
import re
 | 
			
		||||
import math
 | 
			
		||||
import logging
 | 
			
		||||
import os
 | 
			
		||||
from ConfigParser import ConfigParser
 | 
			
		||||
 | 
			
		||||
from six.moves.configparser import ConfigParser
 | 
			
		||||
 | 
			
		||||
import gi
 | 
			
		||||
gi.require_version('Rsvg', '2.0')
 | 
			
		||||
@ -127,8 +129,8 @@ class _SVGLoader(object):
 | 
			
		||||
            if cache:
 | 
			
		||||
                self._cache[file_name] = icon
 | 
			
		||||
 | 
			
		||||
        for entity, value in entities.items():
 | 
			
		||||
            if isinstance(value, basestring):
 | 
			
		||||
        for entity, value in list(entities.items()):
 | 
			
		||||
            if isinstance(value, six.string_types):
 | 
			
		||||
                xml = '<!ENTITY %s "%s">' % (entity, value)
 | 
			
		||||
                icon = re.sub('<!ENTITY %s .*>' % entity, xml, icon)
 | 
			
		||||
            else:
 | 
			
		||||
@ -207,7 +209,7 @@ class _IconBuffer(object):
 | 
			
		||||
            # try read from the .icon file
 | 
			
		||||
            icon_filename = info.get_filename().replace('.svg', '.icon')
 | 
			
		||||
            if icon_filename != info.get_filename() and \
 | 
			
		||||
                os.path.exists(icon_filename):
 | 
			
		||||
                            os.path.exists(icon_filename):
 | 
			
		||||
 | 
			
		||||
                try:
 | 
			
		||||
                    with open(icon_filename) as config_file:
 | 
			
		||||
@ -470,7 +472,6 @@ class Icon(Gtk.Image):
 | 
			
		||||
 | 
			
		||||
    __gtype_name__ = 'SugarIcon'
 | 
			
		||||
 | 
			
		||||
    # FIXME: deprecate icon_size
 | 
			
		||||
    _MENU_SIZES = (Gtk.IconSize.MENU, Gtk.IconSize.DND,
 | 
			
		||||
                   Gtk.IconSize.SMALL_TOOLBAR, Gtk.IconSize.BUTTON)
 | 
			
		||||
 | 
			
		||||
@ -483,7 +484,6 @@ class Icon(Gtk.Image):
 | 
			
		||||
        self._alpha = 1.0
 | 
			
		||||
        self._scale = 1.0
 | 
			
		||||
 | 
			
		||||
        # FIXME: deprecate icon_size
 | 
			
		||||
        if 'icon_size' in kwargs:
 | 
			
		||||
            logging.warning("icon_size is deprecated. Use pixel_size instead.")
 | 
			
		||||
 | 
			
		||||
@ -532,7 +532,6 @@ class Icon(Gtk.Image):
 | 
			
		||||
        if self._buffer.file_name != self.props.file:
 | 
			
		||||
            self._buffer.file_name = self.props.file
 | 
			
		||||
 | 
			
		||||
        # FIXME: deprecate icon_size
 | 
			
		||||
        pixel_size = None
 | 
			
		||||
        if self.props.pixel_size == -1:
 | 
			
		||||
            if self.props.icon_size in self._MENU_SIZES:
 | 
			
		||||
@ -549,7 +548,7 @@ class Icon(Gtk.Image):
 | 
			
		||||
            self._buffer.height = height
 | 
			
		||||
 | 
			
		||||
    def _icon_size_changed_cb(self, image, pspec):
 | 
			
		||||
        self._buffer.icon_size = self.props.icon_size
 | 
			
		||||
        self._buffer.icon_size = self.props.pixel_size
 | 
			
		||||
 | 
			
		||||
    def _icon_name_changed_cb(self, image, pspec):
 | 
			
		||||
        self._buffer.icon_name = self.props.icon_name
 | 
			
		||||
@ -805,7 +804,7 @@ class EventIcon(Gtk.EventBox):
 | 
			
		||||
        # for example, after a touch palette invocation
 | 
			
		||||
        self.connect_after('button-release-event',
 | 
			
		||||
                           self.__button_release_event_cb)
 | 
			
		||||
        for key, value in kwargs.iteritems():
 | 
			
		||||
        for key, value in six.iteritems(kwargs):
 | 
			
		||||
            self.set_property(key, value)
 | 
			
		||||
 | 
			
		||||
        from sugar3.graphics.palette import CursorInvoker
 | 
			
		||||
@ -1152,6 +1151,8 @@ class CanvasIcon(EventIcon):
 | 
			
		||||
 | 
			
		||||
    def __palette_popdown_cb(self, palette):
 | 
			
		||||
        self.unset_state_flags(Gtk.StateFlags.PRELIGHT)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if hasattr(CanvasIcon, 'set_css_name'):
 | 
			
		||||
    CanvasIcon.set_css_name('canvasicon')
 | 
			
		||||
 | 
			
		||||
@ -1447,6 +1448,6 @@ def get_surface(**kwargs):
 | 
			
		||||
        cairo surface or None if image was not found
 | 
			
		||||
    '''
 | 
			
		||||
    icon = _IconBuffer()
 | 
			
		||||
    for key, value in kwargs.items():
 | 
			
		||||
    for key, value in list(kwargs.items()):
 | 
			
		||||
        icon.__setattr__(key, value)
 | 
			
		||||
    return icon.get_surface()
 | 
			
		||||
 | 
			
		||||
@ -61,7 +61,7 @@ class IconEntry(Gtk.Entry):
 | 
			
		||||
        self.set_icon(position, pixbuf)
 | 
			
		||||
 | 
			
		||||
    def set_icon(self, position, pixbuf):
 | 
			
		||||
        if type(pixbuf) is not GdkPixbuf.Pixbuf:
 | 
			
		||||
        if not isinstance(pixbuf, GdkPixbuf.Pixbuf):
 | 
			
		||||
            raise ValueError('Argument must be a pixbuf, not %r.' % pixbuf)
 | 
			
		||||
        self.set_icon_from_pixbuf(position, pixbuf)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -19,8 +19,8 @@
 | 
			
		||||
STABLE.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import six
 | 
			
		||||
import logging
 | 
			
		||||
import StringIO
 | 
			
		||||
import cairo
 | 
			
		||||
 | 
			
		||||
from gi.repository import GObject
 | 
			
		||||
@ -59,13 +59,13 @@ def get_preview_pixbuf(preview_data, width=-1, height=-1):
 | 
			
		||||
        None, if it could not be created
 | 
			
		||||
 | 
			
		||||
    Example:
 | 
			
		||||
        pixbuf = get_preview_pixbuf(metadata.get('preview', ''))                
 | 
			
		||||
        has_preview = pixbuf is not None                                        
 | 
			
		||||
                                                                               
 | 
			
		||||
        if has_preview:                                                        
 | 
			
		||||
            im = Gtk.Image()                                                    
 | 
			
		||||
            im.set_from_pixbuf(pixbuf)                                          
 | 
			
		||||
            box.add(im)                                                        
 | 
			
		||||
        pixbuf = get_preview_pixbuf(metadata.get('preview', ''))
 | 
			
		||||
        has_preview = pixbuf is not None
 | 
			
		||||
 | 
			
		||||
        if has_preview:
 | 
			
		||||
            im = Gtk.Image()
 | 
			
		||||
            im.set_from_pixbuf(pixbuf)
 | 
			
		||||
            box.add(im)
 | 
			
		||||
            im.show()
 | 
			
		||||
    """
 | 
			
		||||
    if width == -1:
 | 
			
		||||
@ -82,7 +82,7 @@ def get_preview_pixbuf(preview_data, width=-1, height=-1):
 | 
			
		||||
            import base64
 | 
			
		||||
            preview_data = base64.b64decode(preview_data)
 | 
			
		||||
 | 
			
		||||
        png_file = StringIO.StringIO(preview_data)
 | 
			
		||||
        png_file = six.StringIO(preview_data)
 | 
			
		||||
        try:
 | 
			
		||||
            # Load image and scale to dimensions
 | 
			
		||||
            surface = cairo.ImageSurface.create_from_png(png_file)
 | 
			
		||||
@ -126,7 +126,7 @@ class ObjectChooser(object):
 | 
			
		||||
        what_filter (str): an activity bundle_id or a generic mime type as
 | 
			
		||||
            defined in :mod:`sugar3.mime` used to determine which objects
 | 
			
		||||
            will be presented in the object chooser
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        filter_type (str): should be one of [None, FILTER_TYPE_GENERIC_MIME,
 | 
			
		||||
            FILTER_TYPE_ACTIVITY, FILTER_TYPE_MIME_BY_ACTIVITY]
 | 
			
		||||
 | 
			
		||||
@ -155,7 +155,7 @@ class ObjectChooser(object):
 | 
			
		||||
 | 
			
		||||
    Examples:
 | 
			
		||||
        chooser = ObjectChooser(self._activity, what_filter='Image')
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        chooser = ObjectChooser(parent=self,
 | 
			
		||||
                                what_filter=self.get_bundle_id(),
 | 
			
		||||
                                filter_type=FILTER_TYPE_ACTIVITY)
 | 
			
		||||
@ -192,7 +192,7 @@ class ObjectChooser(object):
 | 
			
		||||
    def run(self):
 | 
			
		||||
        """
 | 
			
		||||
        Runs the object chooser and displays it.
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            Gtk.ResponseType constant, the response received from displaying the
 | 
			
		||||
                object chooser.
 | 
			
		||||
 | 
			
		||||
@ -36,7 +36,7 @@ def get_group(group_id):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def popdown_all():
 | 
			
		||||
    for group in _groups.values():
 | 
			
		||||
    for group in list(_groups.values()):
 | 
			
		||||
        group.popdown()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -56,7 +56,7 @@ Example:
 | 
			
		||||
                menu_item.show()
 | 
			
		||||
 | 
			
		||||
            def __edit_cb(self, menu_item):
 | 
			
		||||
                print 'Edit...'
 | 
			
		||||
                print('Edit...')
 | 
			
		||||
 | 
			
		||||
        # Usually the Palette instance is returned in a create_palette function
 | 
			
		||||
        p = ItemPalette()
 | 
			
		||||
 | 
			
		||||
@ -43,6 +43,7 @@ from sugar3.graphics.icon import CellRendererIcon
 | 
			
		||||
 | 
			
		||||
_pointer = None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _get_pointer_position(widget):
 | 
			
		||||
    global _pointer
 | 
			
		||||
 | 
			
		||||
@ -53,6 +54,7 @@ def _get_pointer_position(widget):
 | 
			
		||||
    screen, x, y = _pointer.get_position()
 | 
			
		||||
    return (x, y)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _calculate_gap(a, b):
 | 
			
		||||
    """Helper function to find the gap position and size of widget a"""
 | 
			
		||||
    # Test for each side if the palette and invoker are
 | 
			
		||||
@ -420,6 +422,8 @@ class _PaletteWindowWidget(Gtk.Window):
 | 
			
		||||
        self.disconnect_by_func(self.__enter_notify_event_cb)
 | 
			
		||||
        self.disconnect_by_func(self.__leave_notify_event_cb)
 | 
			
		||||
        self.hide()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if hasattr(_PaletteWindowWidget, 'set_css_name'):
 | 
			
		||||
    _PaletteWindowWidget.set_css_name('palette')
 | 
			
		||||
 | 
			
		||||
@ -954,7 +958,7 @@ class Invoker(GObject.GObject):
 | 
			
		||||
            dright = screen_area.x + screen_area.width - rect.x - rect.width
 | 
			
		||||
 | 
			
		||||
            ih = 0
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            if palette_dim.width == 0:
 | 
			
		||||
                ph = 0
 | 
			
		||||
 | 
			
		||||
@ -1315,7 +1319,7 @@ class CursorInvoker(Invoker):
 | 
			
		||||
        self._leave_hid = self._item.connect('leave-notify-event',
 | 
			
		||||
                                             self.__leave_notify_event_cb)
 | 
			
		||||
        self._release_hid = self._item.connect('button-release-event',
 | 
			
		||||
                                               self.__button_release_event_cb)
 | 
			
		||||
                                             self.__button_release_event_cb)
 | 
			
		||||
        self._long_pressed_hid = self._long_pressed_controller.connect(
 | 
			
		||||
            'pressed',
 | 
			
		||||
            self.__long_pressed_event_cb, self._item)
 | 
			
		||||
@ -1325,9 +1329,9 @@ class CursorInvoker(Invoker):
 | 
			
		||||
 | 
			
		||||
    def detach(self):
 | 
			
		||||
        Invoker.detach(self)
 | 
			
		||||
        self._item.disconnect(self._enter_hid)
 | 
			
		||||
        self._item.disconnect(self._leave_hid)
 | 
			
		||||
        self._item.disconnect(self._release_hid)
 | 
			
		||||
        self._item.disconnect_by_func(self.__enter_notify_event_cb)
 | 
			
		||||
        self._item.disconnect_by_func(self.__leave_notify_event_cb)
 | 
			
		||||
        self._item.disconnect_by_func(self.__button_release_event_cb)
 | 
			
		||||
        self._long_pressed_controller.detach(self._item)
 | 
			
		||||
        self._long_pressed_controller.disconnect(self._long_pressed_hid)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,202 +0,0 @@
 | 
			
		||||
# Copyright (C) 2016 Abhijit Patel
 | 
			
		||||
#
 | 
			
		||||
# 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
 | 
			
		||||
 | 
			
		||||
'''
 | 
			
		||||
Provide a PopWindow class for pop-up windows.
 | 
			
		||||
Making PopWindow containing Gtk.Toolbar which also contains Gtk.Label
 | 
			
		||||
and Toolbutton at the end of the Gtk.Toolbar.
 | 
			
		||||
 | 
			
		||||
It is possible to change props like size and add more widgets PopWindow
 | 
			
		||||
and also to Gtk.Toolbar.
 | 
			
		||||
 | 
			
		||||
Example:
 | 
			
		||||
    .. literalinclude: ..sugar/src/jarabe/view/viewsource.py
 | 
			
		||||
    .. literalinclude: ..sugar/src/jarabe/view/viewhelp.py
 | 
			
		||||
'''
 | 
			
		||||
from gettext import gettext as _
 | 
			
		||||
from gi.repository import Gtk
 | 
			
		||||
from gi.repository import Gdk
 | 
			
		||||
from gi.repository import GdkX11
 | 
			
		||||
from gi.repository import GObject
 | 
			
		||||
 | 
			
		||||
from sugar3.graphics import style
 | 
			
		||||
from sugar3.graphics.toolbutton import ToolButton
 | 
			
		||||
 | 
			
		||||
from jarabe.model import shell
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PopWindow(Gtk.Window):
 | 
			
		||||
    """
 | 
			
		||||
    UI interface for activity Pop-up Windows.
 | 
			
		||||
    PopWindows are the windows that open on the top of the current window.
 | 
			
		||||
    These pop-up windows don't cover the whole screen.
 | 
			
		||||
    They contain canvas content, alerts messages, a tray and a
 | 
			
		||||
    toolbar.
 | 
			
		||||
 | 
			
		||||
    FULLSCREEN and HALF_WIDTH for setting size of the window.
 | 
			
		||||
 | 
			
		||||
    Kwargs:
 | 
			
		||||
        size (int,int): size to be set of the window
 | 
			
		||||
        window_xid (xlib.Window): xid of the parent window
 | 
			
		||||
    """
 | 
			
		||||
    FULLSCREEN = (Gdk.Screen.width() - style.GRID_CELL_SIZE * 3,
 | 
			
		||||
                  Gdk.Screen.height() - style.GRID_CELL_SIZE * 2)
 | 
			
		||||
 | 
			
		||||
    HALF_WIDTH = ((Gdk.Screen.height() - style.GRID_CELL_SIZE * 3)/2,
 | 
			
		||||
                  (Gdk.Screen.height() - style.GRID_CELL_SIZE * 2))
 | 
			
		||||
 | 
			
		||||
    def __init__(self, window_xid=None, **kwargs):
 | 
			
		||||
        Gtk.Window.__init__(self, **kwargs)
 | 
			
		||||
        self._parent_window_xid = window_xid
 | 
			
		||||
 | 
			
		||||
        self.set_decorated(False)
 | 
			
		||||
        self.set_position(Gtk.WindowPosition.CENTER_ALWAYS)
 | 
			
		||||
        self.set_border_width(style.LINE_WIDTH)
 | 
			
		||||
        self.set_has_resize_grip(False)
 | 
			
		||||
        self.props.size = self.FULLSCREEN
 | 
			
		||||
 | 
			
		||||
        self.connect('realize', self.__realize_cb)
 | 
			
		||||
        self.connect('key-press-event', self.__key_press_event_cb)
 | 
			
		||||
        self.connect('hide', self.__hide_cb)
 | 
			
		||||
 | 
			
		||||
        self._vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
 | 
			
		||||
        self.add(self._vbox)
 | 
			
		||||
        self._vbox.show()
 | 
			
		||||
 | 
			
		||||
        self._title_box = TitleBox()
 | 
			
		||||
        self._title_box.close_button.connect(
 | 
			
		||||
            'clicked',
 | 
			
		||||
            self.__close_button_clicked_cb)
 | 
			
		||||
        self._title_box.set_size_request(-1, style.GRID_CELL_SIZE)
 | 
			
		||||
 | 
			
		||||
        self._vbox.pack_start(self._title_box, False, True, 0)
 | 
			
		||||
        self._title_box.show()
 | 
			
		||||
 | 
			
		||||
        # Note:
 | 
			
		||||
        # Not displaying the pop-up from here instead allowing
 | 
			
		||||
        # the child class to display the window after modifications
 | 
			
		||||
        # like chaninging window size, decorating, changing position.
 | 
			
		||||
 | 
			
		||||
    def set_size(self, size):
 | 
			
		||||
        width, height = size
 | 
			
		||||
        self.set_size_request(width, height)
 | 
			
		||||
 | 
			
		||||
    size = GObject.Property(type=None, setter=set_size)
 | 
			
		||||
 | 
			
		||||
    def get_title_box(self):
 | 
			
		||||
        '''
 | 
			
		||||
        Getter method for title-box
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            self._title_box (): Title or Tool Box
 | 
			
		||||
        '''
 | 
			
		||||
        return self._title_box
 | 
			
		||||
 | 
			
		||||
    title_box = GObject.Property(type=str, getter=get_title_box)
 | 
			
		||||
 | 
			
		||||
    def get_vbox(self):
 | 
			
		||||
        '''
 | 
			
		||||
        Getter method for canvas
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            self._vbox (Gtk.Box): canvas
 | 
			
		||||
        '''
 | 
			
		||||
        return self._vbox
 | 
			
		||||
    vbox = GObject.Property(type=str, getter=get_vbox)
 | 
			
		||||
 | 
			
		||||
    def __close_button_clicked_cb(self, button):
 | 
			
		||||
        self.destroy()
 | 
			
		||||
 | 
			
		||||
    def __key_press_event_cb(self, window, event):
 | 
			
		||||
        keyname = Gdk.keyval_name(event.keyval)
 | 
			
		||||
        if keyname == 'Escape':
 | 
			
		||||
            self.destroy()
 | 
			
		||||
 | 
			
		||||
    def __realize_cb(self, widget):
 | 
			
		||||
        self.set_type_hint(Gdk.WindowTypeHint.DIALOG)
 | 
			
		||||
        window = self.get_window()
 | 
			
		||||
        window.set_accept_focus(True)
 | 
			
		||||
 | 
			
		||||
        if self._parent_window_xid is not None:
 | 
			
		||||
            display = Gdk.Display.get_default()
 | 
			
		||||
            parent = GdkX11.X11Window.foreign_new_for_display(
 | 
			
		||||
                display, self._parent_window_xid)
 | 
			
		||||
            window.set_transient_for(parent)
 | 
			
		||||
            shell.get_model().push_modal()
 | 
			
		||||
 | 
			
		||||
    def __hide_cb(self, widget):
 | 
			
		||||
        shell.get_model().pop_modal()
 | 
			
		||||
 | 
			
		||||
    def add_view(self, widget, expand=True, fill=True, padding=0):
 | 
			
		||||
        '''
 | 
			
		||||
        Adds child to the vbox.
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            widget (Gtk.Widget): widget to be added
 | 
			
		||||
 | 
			
		||||
            expand (bool): True if child is to be given extra space allocated
 | 
			
		||||
                to vbox.
 | 
			
		||||
 | 
			
		||||
            fill (bool): True if space given to child by the expand option is
 | 
			
		||||
                actually allocated to child, rather than just padding it.
 | 
			
		||||
 | 
			
		||||
            padding (int): extra space in pixels to put between child and its
 | 
			
		||||
                neighbors, over and above the global amount specified
 | 
			
		||||
                by spacing in vbox.
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            None
 | 
			
		||||
        '''
 | 
			
		||||
        self._vbox.pack_start(widget, expand, fill, padding)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TitleBox(Gtk.Toolbar):
 | 
			
		||||
    '''
 | 
			
		||||
    Title box at the top of the pop-up window.
 | 
			
		||||
    Title and close button are added to the box and as needed more widgets
 | 
			
		||||
    can be added using self.add_widget method.
 | 
			
		||||
    This box is optional as the inherited class can remove this block by
 | 
			
		||||
    setting the self._set_title_box to False.
 | 
			
		||||
    '''
 | 
			
		||||
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        Gtk.Toolbar.__init__(self)
 | 
			
		||||
 | 
			
		||||
        self.close_button = ToolButton(icon_name='dialog-cancel')
 | 
			
		||||
        self.close_button.set_tooltip(_('Close'))
 | 
			
		||||
        self.insert(self.close_button, -1)
 | 
			
		||||
        self.close_button.show()
 | 
			
		||||
 | 
			
		||||
        self._label = Gtk.Label()
 | 
			
		||||
        self._label.set_alignment(0, 0.5)
 | 
			
		||||
 | 
			
		||||
        tool_item = Gtk.ToolItem()
 | 
			
		||||
        tool_item.set_expand(True)
 | 
			
		||||
        tool_item.add(self._label)
 | 
			
		||||
        self._label.show()
 | 
			
		||||
        self.insert(tool_item, 0)
 | 
			
		||||
        tool_item.show()
 | 
			
		||||
 | 
			
		||||
    def set_title(self, title):
 | 
			
		||||
        '''
 | 
			
		||||
        setter function for 'title' property.
 | 
			
		||||
        Args:
 | 
			
		||||
           title (str): title for the pop-up window
 | 
			
		||||
        '''
 | 
			
		||||
        self._label.set_markup('<b>%s</b>' % title)
 | 
			
		||||
        self._label.show()
 | 
			
		||||
 | 
			
		||||
    title = GObject.Property(type=str, setter=set_title)
 | 
			
		||||
@ -41,6 +41,7 @@ class ProgressIcon(Gtk.DrawingArea):
 | 
			
		||||
      fill_color (string): The main (inside) color of progressicon
 | 
			
		||||
         [e.g. fill_color=style.COLOR_BLUE.get_svg()
 | 
			
		||||
    '''
 | 
			
		||||
 | 
			
		||||
    def __init__(self, icon_name, pixel_size, stroke_color, fill_color,
 | 
			
		||||
                 direction='vertical'):
 | 
			
		||||
        Gtk.DrawingArea.__init__(self)
 | 
			
		||||
 | 
			
		||||
@ -32,18 +32,18 @@ from gi.repository import GLib
 | 
			
		||||
 | 
			
		||||
class ScrollingDetector(GObject.GObject):
 | 
			
		||||
    '''
 | 
			
		||||
    The scrolling detector sends signals when a scrolled window is scrolled and 
 | 
			
		||||
    The scrolling detector sends signals when a scrolled window is scrolled and
 | 
			
		||||
    when a scrolled window stops scrolling. Only one `scroll-start` signal will be
 | 
			
		||||
	emitted until scrolling stops.
 | 
			
		||||
    
 | 
			
		||||
        emitted until scrolling stops.
 | 
			
		||||
 | 
			
		||||
    The `scroll-start` signal is emitted when scrolling begins and
 | 
			
		||||
    The `scroll-end` signal is emitted when scrolling ends
 | 
			
		||||
    Neither of these two signals have any arguments
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        scrolled_window (Gtk.ScrolledWindow): A GTK scrolled window object for which
 | 
			
		||||
            scrolling is to be detected
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        timeout (int): time in milliseconds to establish the interval for which
 | 
			
		||||
            scrolling is detected
 | 
			
		||||
    '''
 | 
			
		||||
@ -65,7 +65,7 @@ class ScrollingDetector(GObject.GObject):
 | 
			
		||||
        Connects scrolling detector to a scrolled window.
 | 
			
		||||
        Detects scrolling when the vertical scrollbar
 | 
			
		||||
        adjustment value is changed
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        Should be used to link an instance of a scrolling detector
 | 
			
		||||
        to a Scrolled Window, after setting scrolled_window
 | 
			
		||||
        '''
 | 
			
		||||
 | 
			
		||||
@ -59,6 +59,7 @@ class Font(object):
 | 
			
		||||
    Args:
 | 
			
		||||
        desc (str): a description of the Font object
 | 
			
		||||
    '''
 | 
			
		||||
 | 
			
		||||
    def __init__(self, desc):
 | 
			
		||||
        self._desc = desc
 | 
			
		||||
 | 
			
		||||
@ -84,6 +85,7 @@ class Color(object):
 | 
			
		||||
 | 
			
		||||
        alpha (double):  transparency of color
 | 
			
		||||
    '''
 | 
			
		||||
 | 
			
		||||
    def __init__(self, color, alpha=1.0):
 | 
			
		||||
        self._r, self._g, self._b = self._html_to_rgb(color)
 | 
			
		||||
        self._a = alpha
 | 
			
		||||
@ -112,7 +114,8 @@ class Color(object):
 | 
			
		||||
        '''
 | 
			
		||||
        Returns string in the standard html Color format (#FFFFFF)
 | 
			
		||||
        '''
 | 
			
		||||
        return '#%02x%02x%02x' % (self._r * 255, self._g * 255, self._b * 255)
 | 
			
		||||
        return '#%02x%02x%02x' % (
 | 
			
		||||
            int(self._r * 255), int(self._g * 255), int(self._b * 255))
 | 
			
		||||
 | 
			
		||||
    def _html_to_rgb(self, html_color):
 | 
			
		||||
        '''
 | 
			
		||||
 | 
			
		||||
@ -203,6 +203,8 @@ class ToolbarBox(Gtk.VBox):
 | 
			
		||||
        if button == self.expanded_button:
 | 
			
		||||
            self.remove(button.page_widget)
 | 
			
		||||
            self._expanded_button_index = -1
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if hasattr(ToolbarBox, 'set_css_name'):
 | 
			
		||||
    ToolbarBox.set_css_name('toolbarbox')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -32,7 +32,7 @@ class Toolbox(Gtk.VBox):
 | 
			
		||||
    Class to represent the toolbox of an activity. Groups a
 | 
			
		||||
    number of toolbars vertically, which can be accessed using their
 | 
			
		||||
    indices. The current toolbar is the only one displayed.
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    Emits `current-toolbar-changed` signal when the
 | 
			
		||||
    current toolbar is changed. This signal takes the current page index
 | 
			
		||||
    as an argument.
 | 
			
		||||
@ -71,13 +71,13 @@ class Toolbox(Gtk.VBox):
 | 
			
		||||
    def add_toolbar(self, name, toolbar):
 | 
			
		||||
        '''
 | 
			
		||||
        Adds a toolbar to this toolbox. Toolbar will be added
 | 
			
		||||
        to the end of this toolbox, and it's index will be 
 | 
			
		||||
        to the end of this toolbox, and it's index will be
 | 
			
		||||
        1 greater than the previously added index (index will be
 | 
			
		||||
        0 if it is the first toolbar added).
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            name (string): name of toolbar to be added
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            toolbar (.. :class:`Gtk.Toolbar`): Gtk.Toolbar to be appended to this toolbox
 | 
			
		||||
        '''
 | 
			
		||||
        label = Gtk.Label(label=name)
 | 
			
		||||
@ -106,7 +106,7 @@ class Toolbox(Gtk.VBox):
 | 
			
		||||
    def remove_toolbar(self, index):
 | 
			
		||||
        '''
 | 
			
		||||
        Removes toolbar at the index specified.
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            index (int): index of the toolbar to be removed
 | 
			
		||||
        '''
 | 
			
		||||
@ -118,9 +118,9 @@ class Toolbox(Gtk.VBox):
 | 
			
		||||
 | 
			
		||||
    def set_current_toolbar(self, index):
 | 
			
		||||
        '''
 | 
			
		||||
        Sets the current toolbar to that of the index specified and 
 | 
			
		||||
        Sets the current toolbar to that of the index specified and
 | 
			
		||||
        displays it.
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            index (int): index of toolbar to be set as current toolbar
 | 
			
		||||
        '''
 | 
			
		||||
 | 
			
		||||
@ -27,7 +27,7 @@ Example:
 | 
			
		||||
        from sugar3.graphics.toolbutton import ToolButton
 | 
			
		||||
 | 
			
		||||
        def __clicked_cb(button):
 | 
			
		||||
            print "tool button was clicked"
 | 
			
		||||
            print("tool button was clicked")
 | 
			
		||||
 | 
			
		||||
        w = Gtk.Window()
 | 
			
		||||
        w.connect('destroy', Gtk.main_quit)
 | 
			
		||||
 | 
			
		||||
@ -330,6 +330,8 @@ class HTray(Gtk.EventBox):
 | 
			
		||||
 | 
			
		||||
    def scroll_to_item(self, item):
 | 
			
		||||
        self._viewport.scroll_to_item(item)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if hasattr(HTray, 'set_css_name'):
 | 
			
		||||
    HTray.set_css_name('htray')
 | 
			
		||||
 | 
			
		||||
@ -424,6 +426,8 @@ class VTray(Gtk.EventBox):
 | 
			
		||||
 | 
			
		||||
    def scroll_to_item(self, item):
 | 
			
		||||
        self._viewport.scroll_to_item(item)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if hasattr(VTray, 'set_css_name'):
 | 
			
		||||
    VTray.set_css_name('VTray')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -20,6 +20,7 @@ This class represents all of the colors that the XO can take on.
 | 
			
		||||
Each pair of colors represents the fill color and the stroke color
 | 
			
		||||
'''
 | 
			
		||||
 | 
			
		||||
import six
 | 
			
		||||
import random
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
@ -210,11 +211,11 @@ colors = [['#B20008', '#FF2B34'],
 | 
			
		||||
def _parse_string(color_string):
 | 
			
		||||
    '''
 | 
			
		||||
    Returns array of length 2 of two colors in standard html form of [stroke color, fill color]
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        color_string (string): two html format strings separated by a comma
 | 
			
		||||
    '''
 | 
			
		||||
    if not isinstance(color_string, (str, unicode)):
 | 
			
		||||
    if not isinstance(color_string, (six.text_type, six.binary_type)):
 | 
			
		||||
        logging.error('Invalid color string: %r', color_string)
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
@ -233,13 +234,14 @@ def _parse_string(color_string):
 | 
			
		||||
class XoColor:
 | 
			
		||||
    '''
 | 
			
		||||
    Defines color for XO
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        color_string (string): two html format strings separated 
 | 
			
		||||
		    by a comma, "white", or "insensitive". If color_string 
 | 
			
		||||
		    is None, the user's color will be created. If parsed_color 
 | 
			
		||||
		    cannot be created, a random color will be used
 | 
			
		||||
        color_string (string): two html format strings separated
 | 
			
		||||
                    by a comma, "white", or "insensitive". If color_string
 | 
			
		||||
                    is None, the user's color will be created. If parsed_color
 | 
			
		||||
                    cannot be created, a random color will be used
 | 
			
		||||
    '''
 | 
			
		||||
 | 
			
		||||
    def __init__(self, color_string=None):
 | 
			
		||||
        parsed_color = None
 | 
			
		||||
 | 
			
		||||
@ -261,7 +263,7 @@ class XoColor:
 | 
			
		||||
        '''
 | 
			
		||||
        Compares two XO colors by their stroke and fill color
 | 
			
		||||
        Returns 0 if they are equal and -1 if they are unequal
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            other (object): other XO color to compare
 | 
			
		||||
        '''
 | 
			
		||||
@ -295,12 +297,12 @@ if __name__ == '__main__':
 | 
			
		||||
 | 
			
		||||
    f = open(sys.argv[1], 'r')
 | 
			
		||||
 | 
			
		||||
    print 'colors = ['
 | 
			
		||||
    print('colors = [')
 | 
			
		||||
 | 
			
		||||
    for line in f.readlines():
 | 
			
		||||
        match = re.match(r'fill: ([A-Z0-9]*) stroke: ([A-Z0-9]*)', line)
 | 
			
		||||
        print "['#%s', '#%s'], \\" % (match.group(2), match.group(1))
 | 
			
		||||
        print("['#%s', '#%s'], \\" % (match.group(2), match.group(1)))
 | 
			
		||||
 | 
			
		||||
    print ']'
 | 
			
		||||
    print(']')
 | 
			
		||||
 | 
			
		||||
    f.close()
 | 
			
		||||
 | 
			
		||||
@ -20,16 +20,17 @@
 | 
			
		||||
STABLE.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import six
 | 
			
		||||
import array
 | 
			
		||||
import collections
 | 
			
		||||
import errno
 | 
			
		||||
import logging
 | 
			
		||||
import sys
 | 
			
		||||
import os
 | 
			
		||||
import repr as repr_
 | 
			
		||||
import decorator
 | 
			
		||||
import time
 | 
			
		||||
 | 
			
		||||
from six.moves import reprlib as repr_
 | 
			
		||||
from sugar3 import env
 | 
			
		||||
 | 
			
		||||
# Let's keep this module self contained so that it can be easily
 | 
			
		||||
@ -104,8 +105,8 @@ def cleanup():
 | 
			
		||||
            for f in os.listdir(root):
 | 
			
		||||
                os.remove(os.path.join(root, f))
 | 
			
		||||
            os.rmdir(root)
 | 
			
		||||
        except OSError, e:
 | 
			
		||||
            print "Could not remove old logs files %s" % e
 | 
			
		||||
        except OSError as e:
 | 
			
		||||
            print("Could not remove old logs files %s" % e)
 | 
			
		||||
 | 
			
		||||
    if len(backup_logs) > 0:
 | 
			
		||||
        name = str(int(time.time()))
 | 
			
		||||
@ -116,7 +117,7 @@ def cleanup():
 | 
			
		||||
                source_path = os.path.join(logs_dir, log)
 | 
			
		||||
                dest_path = os.path.join(backup_dir, log)
 | 
			
		||||
                os.rename(source_path, dest_path)
 | 
			
		||||
        except OSError, e:
 | 
			
		||||
        except OSError as e:
 | 
			
		||||
            # gracefully deal w/ disk full
 | 
			
		||||
            if e.errno != errno.ENOSPC:
 | 
			
		||||
                raise e
 | 
			
		||||
@ -145,7 +146,7 @@ def start(log_filename=None):
 | 
			
		||||
        def write(self, s):
 | 
			
		||||
            try:
 | 
			
		||||
                self._stream.write(s)
 | 
			
		||||
            except IOError, e:
 | 
			
		||||
            except IOError as e:
 | 
			
		||||
                # gracefully deal w/ disk full
 | 
			
		||||
                if e.errno != errno.ENOSPC:
 | 
			
		||||
                    raise e
 | 
			
		||||
@ -153,7 +154,7 @@ def start(log_filename=None):
 | 
			
		||||
        def flush(self):
 | 
			
		||||
            try:
 | 
			
		||||
                self._stream.flush()
 | 
			
		||||
            except IOError, e:
 | 
			
		||||
            except IOError as e:
 | 
			
		||||
                # gracefully deal w/ disk full
 | 
			
		||||
                if e.errno != errno.ENOSPC:
 | 
			
		||||
                    raise e
 | 
			
		||||
@ -177,7 +178,7 @@ def start(log_filename=None):
 | 
			
		||||
 | 
			
		||||
            sys.stdout = SafeLogWrapper(sys.stdout)
 | 
			
		||||
            sys.stderr = SafeLogWrapper(sys.stderr)
 | 
			
		||||
        except OSError, e:
 | 
			
		||||
        except OSError as e:
 | 
			
		||||
            # if we're out of space, just continue
 | 
			
		||||
            if e.errno != errno.ENOSPC:
 | 
			
		||||
                raise e
 | 
			
		||||
@ -188,13 +189,15 @@ def start(log_filename=None):
 | 
			
		||||
class TraceRepr(repr_.Repr):
 | 
			
		||||
 | 
			
		||||
    # better handling of subclasses of basic types, e.g. for DBus
 | 
			
		||||
    _TYPES = [int, long, bool, tuple, list, array.array, set, frozenset,
 | 
			
		||||
    _TYPES = [int, bool, tuple, list, array.array, set, frozenset,
 | 
			
		||||
              collections.deque, dict, str]
 | 
			
		||||
    if six.PY2:
 | 
			
		||||
        _TYPES.append(long)
 | 
			
		||||
 | 
			
		||||
    def repr1(self, x, level):
 | 
			
		||||
        for t in self._TYPES:
 | 
			
		||||
            if isinstance(x, t):
 | 
			
		||||
                return getattr(self, 'repr_'+t.__name__)(x, level)
 | 
			
		||||
                return getattr(self, 'repr_' + t.__name__)(x, level)
 | 
			
		||||
 | 
			
		||||
        return repr_.Repr.repr1(self, x, level)
 | 
			
		||||
 | 
			
		||||
@ -232,14 +235,14 @@ def trace(logger=None, logger_name=None, skip_args=None, skip_kwargs=None,
 | 
			
		||||
            [trace_repr.repr(a)
 | 
			
		||||
                for (idx, a) in enumerate(args) if idx not in skip_args] +
 | 
			
		||||
            ['%s=%s' % (k, trace_repr.repr(v))
 | 
			
		||||
                for (k, v) in kwargs.items() if k not in skip_kwargs])
 | 
			
		||||
                for (k, v) in list(kwargs.items()) if k not in skip_kwargs])
 | 
			
		||||
 | 
			
		||||
        trace_logger.log(TRACE, "%s(%s) invoked", f.__name__,
 | 
			
		||||
                         params_formatted)
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            res = f(*args, **kwargs)
 | 
			
		||||
        except:
 | 
			
		||||
        except BaseException:
 | 
			
		||||
            trace_logger.exception("Exception occurred in %s" % f.__name__)
 | 
			
		||||
            raise
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -32,7 +32,9 @@ from gi.repository import GLib
 | 
			
		||||
from gi.repository import GdkPixbuf
 | 
			
		||||
from gi.repository import Gio
 | 
			
		||||
 | 
			
		||||
_ = lambda msg: gettext.dgettext('sugar-toolkit-gtk3', msg)
 | 
			
		||||
 | 
			
		||||
def _(msg): return gettext.dgettext('sugar-toolkit-gtk3', msg)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
GENERIC_TYPE_TEXT = 'Text'
 | 
			
		||||
GENERIC_TYPE_IMAGE = 'Image'
 | 
			
		||||
@ -185,14 +187,14 @@ def get_mime_parents(mime_type):
 | 
			
		||||
            with open(subclasses_path) as parents_file:
 | 
			
		||||
                for line in parents_file:
 | 
			
		||||
                    subclass, parent = line.split()
 | 
			
		||||
                    if subclass not in _subclasses.keys():
 | 
			
		||||
                    if subclass not in list(_subclasses.keys()):
 | 
			
		||||
                        _subclasses[subclass] = [parent]
 | 
			
		||||
                    else:
 | 
			
		||||
                        _subclasses[subclass].append(parent)
 | 
			
		||||
 | 
			
		||||
        _subclasses_timestamps = timestamps
 | 
			
		||||
 | 
			
		||||
    if mime_type in _subclasses.keys():
 | 
			
		||||
    if mime_type in list(_subclasses.keys()):
 | 
			
		||||
        return _subclasses[mime_type]
 | 
			
		||||
    else:
 | 
			
		||||
        return []
 | 
			
		||||
 | 
			
		||||
@ -21,14 +21,14 @@ STABLE.
 | 
			
		||||
 | 
			
		||||
import os
 | 
			
		||||
import threading
 | 
			
		||||
import urllib
 | 
			
		||||
from six.moves import urllib
 | 
			
		||||
import fcntl
 | 
			
		||||
import tempfile
 | 
			
		||||
 | 
			
		||||
from gi.repository import GObject
 | 
			
		||||
from gi.repository import GLib
 | 
			
		||||
import SimpleHTTPServer
 | 
			
		||||
import SocketServer
 | 
			
		||||
from six.moves import SimpleHTTPServer
 | 
			
		||||
from six.moves import socketserver
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
__authinfos = {}
 | 
			
		||||
@ -46,7 +46,7 @@ def _del_authinfo():
 | 
			
		||||
    del __authinfos[threading.currentThread()]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class GlibTCPServer(SocketServer.TCPServer):
 | 
			
		||||
class GlibTCPServer(socketserver.TCPServer):
 | 
			
		||||
    """GlibTCPServer
 | 
			
		||||
 | 
			
		||||
    Integrate socket accept into glib mainloop.
 | 
			
		||||
@ -56,7 +56,7 @@ class GlibTCPServer(SocketServer.TCPServer):
 | 
			
		||||
    request_queue_size = 20
 | 
			
		||||
 | 
			
		||||
    def __init__(self, server_address, RequestHandlerClass):
 | 
			
		||||
        SocketServer.TCPServer.__init__(self, server_address,
 | 
			
		||||
        socketserver.TCPServer.__init__(self, server_address,
 | 
			
		||||
                                        RequestHandlerClass)
 | 
			
		||||
        self.socket.setblocking(0)  # Set nonblocking
 | 
			
		||||
 | 
			
		||||
@ -212,7 +212,7 @@ class GlibURLDownloader(GObject.GObject):
 | 
			
		||||
        GObject.GObject.__init__(self)
 | 
			
		||||
 | 
			
		||||
    def start(self, destfile=None, destfd=None):
 | 
			
		||||
        self._info = urllib.urlopen(self._url)
 | 
			
		||||
        self._info = urllib.request.urlopen(self._url)
 | 
			
		||||
        self._outf = None
 | 
			
		||||
        self._fname = None
 | 
			
		||||
        if destfd and not destfile:
 | 
			
		||||
@ -226,14 +226,14 @@ class GlibURLDownloader(GObject.GObject):
 | 
			
		||||
                self._outf = destfd
 | 
			
		||||
            else:
 | 
			
		||||
                self._outf = os.open(self._fname, os.O_RDWR |
 | 
			
		||||
                                     os.O_TRUNC | os.O_CREAT, 0644)
 | 
			
		||||
                                     os.O_TRUNC | os.O_CREAT, 0o644)
 | 
			
		||||
        else:
 | 
			
		||||
            fname = self._get_filename_from_headers(self._info.headers)
 | 
			
		||||
            self._suggested_fname = fname
 | 
			
		||||
            garbage_, path = urllib.splittype(self._url)
 | 
			
		||||
            garbage_, path = urllib.splithost(path or "")
 | 
			
		||||
            path, garbage_ = urllib.splitquery(path or "")
 | 
			
		||||
            path, garbage_ = urllib.splitattr(path or "")
 | 
			
		||||
            garbage_, path = urllib.parse.splittype(self._url)
 | 
			
		||||
            garbage_, path = urllib.parse.splithost(path or "")
 | 
			
		||||
            path, garbage_ = urllib.parse.splitquery(path or "")
 | 
			
		||||
            path, garbage_ = urllib.parse.splitattr(path or "")
 | 
			
		||||
            suffix = os.path.splitext(path)[1]
 | 
			
		||||
            (self._outf, self._fname) = tempfile.mkstemp(suffix=suffix,
 | 
			
		||||
                                                         dir=self._destdir)
 | 
			
		||||
@ -291,7 +291,7 @@ class GlibURLDownloader(GObject.GObject):
 | 
			
		||||
                self.cleanup()
 | 
			
		||||
                self.emit('finished', self._fname, self._suggested_fname)
 | 
			
		||||
                return False
 | 
			
		||||
        except Exception, err:
 | 
			
		||||
        except Exception as err:
 | 
			
		||||
            self.cleanup(remove=True)
 | 
			
		||||
            self.emit('error', 'Error downloading file: %r' % err)
 | 
			
		||||
            return False
 | 
			
		||||
 | 
			
		||||
@ -21,6 +21,7 @@
 | 
			
		||||
STABLE.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import six
 | 
			
		||||
import logging
 | 
			
		||||
from functools import partial
 | 
			
		||||
 | 
			
		||||
@ -124,6 +125,11 @@ class Activity(GObject.GObject):
 | 
			
		||||
 | 
			
		||||
    def _start_tracking_properties(self):
 | 
			
		||||
        bus = dbus.SessionBus()
 | 
			
		||||
        arg_dict = dict(reply_handler=self.__got_properties_cb,
 | 
			
		||||
                        error_handler=self.__error_handler_cb)
 | 
			
		||||
        if six.PY2:
 | 
			
		||||
            arg_dict = arg_dict.update(utf8_strings=True)
 | 
			
		||||
 | 
			
		||||
        self._get_properties_call = bus.call_async(
 | 
			
		||||
            self.telepathy_conn.requested_bus_name,
 | 
			
		||||
            self.telepathy_conn.object_path,
 | 
			
		||||
@ -131,9 +137,7 @@ class Activity(GObject.GObject):
 | 
			
		||||
            'GetProperties',
 | 
			
		||||
            'u',
 | 
			
		||||
            (self.room_handle,),
 | 
			
		||||
            reply_handler=self.__got_properties_cb,
 | 
			
		||||
            error_handler=self.__error_handler_cb,
 | 
			
		||||
            utf8_strings=True)
 | 
			
		||||
            arg_dict)
 | 
			
		||||
 | 
			
		||||
        # As only one Activity instance is needed per activity process,
 | 
			
		||||
        # we can afford listening to ActivityPropertiesChanged like this.
 | 
			
		||||
@ -144,7 +148,7 @@ class Activity(GObject.GObject):
 | 
			
		||||
 | 
			
		||||
    def __activity_properties_changed_cb(self, room_handle, properties):
 | 
			
		||||
        _logger.debug('%r: Activity properties changed to %r' % (self,
 | 
			
		||||
                      properties))
 | 
			
		||||
                                                                 properties))
 | 
			
		||||
        self._update_properties(properties)
 | 
			
		||||
 | 
			
		||||
    def __got_properties_cb(self, properties):
 | 
			
		||||
@ -239,7 +243,7 @@ class Activity(GObject.GObject):
 | 
			
		||||
        returns list of presence Buddy objects that we can successfully
 | 
			
		||||
        create from the buddy object paths that PS has for this activity.
 | 
			
		||||
        """
 | 
			
		||||
        return self._joined_buddies.values()
 | 
			
		||||
        return list(self._joined_buddies.values())
 | 
			
		||||
 | 
			
		||||
    def get_buddy_by_handle(self, handle):
 | 
			
		||||
        """Retrieve the Buddy object given a telepathy handle.
 | 
			
		||||
@ -300,8 +304,9 @@ class Activity(GObject.GObject):
 | 
			
		||||
        channel.connect_to_signal('Closed', self.__text_channel_closed_cb)
 | 
			
		||||
 | 
			
		||||
    def __get_all_members_cb(self, members, local_pending, remote_pending):
 | 
			
		||||
        _logger.debug('__get_all_members_cb %r %r' % (members,
 | 
			
		||||
                      self._text_channel_group_flags))
 | 
			
		||||
        _logger.debug(
 | 
			
		||||
            '__get_all_members_cb %r %r' %
 | 
			
		||||
            (members, self._text_channel_group_flags))
 | 
			
		||||
        if self._channel_self_handle in members:
 | 
			
		||||
            members.remove(self._channel_self_handle)
 | 
			
		||||
 | 
			
		||||
@ -631,8 +636,9 @@ class _JoinCommand(_BaseCommand):
 | 
			
		||||
        self._add_self_to_channel()
 | 
			
		||||
 | 
			
		||||
    def __text_channel_group_flags_changed_cb(self, added, removed):
 | 
			
		||||
        _logger.debug('__text_channel_group_flags_changed_cb %r %r' % (added,
 | 
			
		||||
                      removed))
 | 
			
		||||
        _logger.debug(
 | 
			
		||||
            '__text_channel_group_flags_changed_cb %r %r' %
 | 
			
		||||
            (added, removed))
 | 
			
		||||
        self.text_channel_group_flags |= added
 | 
			
		||||
        self.text_channel_group_flags &= ~removed
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -22,7 +22,7 @@ STABLE.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
import six
 | 
			
		||||
from gi.repository import GObject
 | 
			
		||||
import dbus
 | 
			
		||||
from telepathy.interfaces import CONNECTION, \
 | 
			
		||||
@ -103,7 +103,7 @@ class BaseBuddy(GObject.GObject):
 | 
			
		||||
    def get_current_activity(self):
 | 
			
		||||
        if self._current_activity is None:
 | 
			
		||||
            return None
 | 
			
		||||
        for activity in self._activities.values():
 | 
			
		||||
        for activity in list(self._activities.values()):
 | 
			
		||||
            if activity.props.id == self._current_activity:
 | 
			
		||||
                return activity
 | 
			
		||||
        return None
 | 
			
		||||
@ -164,6 +164,12 @@ class Buddy(BaseBuddy):
 | 
			
		||||
                                     dbus_interface=CONNECTION)
 | 
			
		||||
        self.contact_handle = handles[0]
 | 
			
		||||
 | 
			
		||||
        arg_dict = dict(reply_handler=self.__got_properties_cb,
 | 
			
		||||
                        error_handler=self.__error_handler_cb,
 | 
			
		||||
                        byte_arrays = True)
 | 
			
		||||
        if six.PY2:
 | 
			
		||||
            arg_dict = arg_dict.update(utf8_strings=True)
 | 
			
		||||
 | 
			
		||||
        self._get_properties_call = bus.call_async(
 | 
			
		||||
            connection_name,
 | 
			
		||||
            connection.object_path,
 | 
			
		||||
@ -171,10 +177,7 @@ class Buddy(BaseBuddy):
 | 
			
		||||
            'GetProperties',
 | 
			
		||||
            'u',
 | 
			
		||||
            (self.contact_handle,),
 | 
			
		||||
            reply_handler=self.__got_properties_cb,
 | 
			
		||||
            error_handler=self.__error_handler_cb,
 | 
			
		||||
            utf8_strings=True,
 | 
			
		||||
            byte_arrays=True)
 | 
			
		||||
            arg_dict)
 | 
			
		||||
 | 
			
		||||
        self._get_attributes_call = bus.call_async(
 | 
			
		||||
            connection_name,
 | 
			
		||||
@ -245,4 +248,3 @@ class Owner(BaseBuddy):
 | 
			
		||||
 | 
			
		||||
        self.props.nick = get_nick_name()
 | 
			
		||||
        self.props.color = get_color().to_string()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -92,7 +92,8 @@ class ConnectionManager(object):
 | 
			
		||||
 | 
			
		||||
    def get_preferred_connection(self):
 | 
			
		||||
        best_connection = None, None
 | 
			
		||||
        for account_path, connection in self._connections_per_account.items():
 | 
			
		||||
        for account_path, connection in list(
 | 
			
		||||
                self._connections_per_account.items()):
 | 
			
		||||
            if 'salut' in account_path and connection.connected:
 | 
			
		||||
                best_connection = account_path, connection.connection
 | 
			
		||||
            elif 'gabble' in account_path and connection.connected:
 | 
			
		||||
@ -107,7 +108,8 @@ class ConnectionManager(object):
 | 
			
		||||
        return self._connections_per_account
 | 
			
		||||
 | 
			
		||||
    def get_account_for_connection(self, connection_path):
 | 
			
		||||
        for account_path, connection in self._connections_per_account.items():
 | 
			
		||||
        for account_path, connection in list(
 | 
			
		||||
                self._connections_per_account.items()):
 | 
			
		||||
            if connection.connection.object_path == connection_path:
 | 
			
		||||
                return account_path
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
@ -78,7 +78,8 @@ class PresenceService(GObject.GObject):
 | 
			
		||||
            connection_manager = get_connection_manager()
 | 
			
		||||
            connections_per_account = \
 | 
			
		||||
                connection_manager.get_connections_per_account()
 | 
			
		||||
            for account_path, connection in connections_per_account.items():
 | 
			
		||||
            for account_path, connection in list(
 | 
			
		||||
                    connections_per_account.items()):
 | 
			
		||||
                if not connection.connected:
 | 
			
		||||
                    continue
 | 
			
		||||
                logging.debug('Calling GetActivity on %s' % account_path)
 | 
			
		||||
@ -86,13 +87,13 @@ class PresenceService(GObject.GObject):
 | 
			
		||||
                    room_handle = connection.connection.GetActivity(
 | 
			
		||||
                        activity_id,
 | 
			
		||||
                        dbus_interface=CONN_INTERFACE_ACTIVITY_PROPERTIES)
 | 
			
		||||
                except dbus.exceptions.DBusException, e:
 | 
			
		||||
                except dbus.exceptions.DBusException as e:
 | 
			
		||||
                    name = 'org.freedesktop.Telepathy.Error.NotAvailable'
 | 
			
		||||
                    if e.get_dbus_name() == name:
 | 
			
		||||
                        logging.debug("There's no shared activity with the id "
 | 
			
		||||
                                      "%s" % activity_id)
 | 
			
		||||
                    elif e.get_dbus_name() == \
 | 
			
		||||
                         'org.freedesktop.DBus.Error.UnknownMethod':
 | 
			
		||||
                            'org.freedesktop.DBus.Error.UnknownMethod':
 | 
			
		||||
                        logging.warning(
 | 
			
		||||
                            'Telepathy Account %r does not support '
 | 
			
		||||
                            'Sugar collaboration', account_path)
 | 
			
		||||
 | 
			
		||||
@ -24,6 +24,7 @@ __all__ = ('TubeConnection', )
 | 
			
		||||
__docformat__ = 'reStructuredText'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
import six
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
from dbus.connection import Connection
 | 
			
		||||
@ -77,7 +78,9 @@ class TubeConnection(Connection):
 | 
			
		||||
 | 
			
		||||
    def close(self):
 | 
			
		||||
        self._dbus_names_changed_match.remove()
 | 
			
		||||
        self._on_dbus_names_changed(self.tube_id, (), self.participants.keys())
 | 
			
		||||
        self._on_dbus_names_changed(
 | 
			
		||||
            self.tube_id, (), list(
 | 
			
		||||
                self.participants.keys()))
 | 
			
		||||
        super(TubeConnection, self).close()
 | 
			
		||||
 | 
			
		||||
    def _on_get_dbus_names_reply(self, names):
 | 
			
		||||
@ -111,6 +114,6 @@ class TubeConnection(Connection):
 | 
			
		||||
            # GetDBusNames already returned: fake a participant add event
 | 
			
		||||
            # immediately
 | 
			
		||||
            added = []
 | 
			
		||||
            for k, v in self.participants.iteritems():
 | 
			
		||||
            for k, v in six.iteritems(self.participants):
 | 
			
		||||
                added.append((k, v))
 | 
			
		||||
            callback(added, [])
 | 
			
		||||
 | 
			
		||||
@ -21,7 +21,8 @@
 | 
			
		||||
from gi.repository import Gio
 | 
			
		||||
import os
 | 
			
		||||
import logging
 | 
			
		||||
from ConfigParser import ConfigParser
 | 
			
		||||
 | 
			
		||||
from six.moves.configparser import ConfigParser
 | 
			
		||||
 | 
			
		||||
from sugar3 import env
 | 
			
		||||
from sugar3 import util
 | 
			
		||||
 | 
			
		||||
@ -33,7 +33,7 @@ try:
 | 
			
		||||
    from gi.repository import Gst
 | 
			
		||||
    Gst.init(None)
 | 
			
		||||
    Gst.parse_launch('espeak')
 | 
			
		||||
except:
 | 
			
		||||
except BaseException:
 | 
			
		||||
    logging.error('Gst or the espeak plugin is not installed in the system.')
 | 
			
		||||
    _HAS_GST = False
 | 
			
		||||
 | 
			
		||||
@ -268,7 +268,12 @@ class SpeechManager(GObject.GObject):
 | 
			
		||||
        else:
 | 
			
		||||
            voice_name = self._player.get_all_voices()[lang_code]
 | 
			
		||||
        if text:
 | 
			
		||||
            logging.error('PLAYING %r lang %r pitch %r rate %r', text, voice_name, pitch, rate)
 | 
			
		||||
            logging.error(
 | 
			
		||||
                'PLAYING %r lang %r pitch %r rate %r',
 | 
			
		||||
                text,
 | 
			
		||||
                voice_name,
 | 
			
		||||
                pitch,
 | 
			
		||||
                rate)
 | 
			
		||||
            self._player.speak(pitch, rate, voice_name, text)
 | 
			
		||||
 | 
			
		||||
    def say_selected_text(self):
 | 
			
		||||
 | 
			
		||||
@ -3,4 +3,4 @@ sugar_PYTHON = \
 | 
			
		||||
	__init__.py \
 | 
			
		||||
    discover.py \
 | 
			
		||||
	uitree.py \
 | 
			
		||||
	unittest.py
 | 
			
		||||
	_unittest.py
 | 
			
		||||
 | 
			
		||||
@ -18,8 +18,6 @@
 | 
			
		||||
UNSTABLE.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from __future__ import absolute_import
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
import os
 | 
			
		||||
import unittest
 | 
			
		||||
@ -52,11 +50,11 @@ class UITestCase(unittest.TestCase):
 | 
			
		||||
    @contextmanager
 | 
			
		||||
    def run_view(self, name):
 | 
			
		||||
        view_path = os.path.join("views", "%s.py" % name)
 | 
			
		||||
        process = subprocess.Popen(["python", view_path])
 | 
			
		||||
        process = subprocess.Popen(["python3", view_path])
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            yield
 | 
			
		||||
        except:
 | 
			
		||||
        except BaseException:
 | 
			
		||||
            logging.debug(uitree.get_root().dump())
 | 
			
		||||
            raise
 | 
			
		||||
        finally:
 | 
			
		||||
@ -77,12 +75,12 @@ class UITestCase(unittest.TestCase):
 | 
			
		||||
                cmd += options
 | 
			
		||||
            process = subprocess.Popen(cmd)
 | 
			
		||||
        else:
 | 
			
		||||
            print "No bundle_id specified."
 | 
			
		||||
            print("No bundle_id specified.")
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            yield
 | 
			
		||||
        except:
 | 
			
		||||
        except BaseException:
 | 
			
		||||
            logging.debug(uitree.get_root().dump())
 | 
			
		||||
            raise
 | 
			
		||||
        finally:
 | 
			
		||||
@ -18,8 +18,6 @@
 | 
			
		||||
UNSTABLE.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from __future__ import absolute_import
 | 
			
		||||
 | 
			
		||||
import argparse
 | 
			
		||||
import sys
 | 
			
		||||
import os
 | 
			
		||||
 | 
			
		||||
@ -44,7 +44,7 @@ def _retry_find(func):
 | 
			
		||||
 | 
			
		||||
            try:
 | 
			
		||||
                result = func(*args, **kwargs)
 | 
			
		||||
            except GLib.GError, e:
 | 
			
		||||
            except GLib.GError as e:
 | 
			
		||||
                # The application is not responding, try again
 | 
			
		||||
                if e.code == Atspi.Error.IPC:
 | 
			
		||||
                    continue
 | 
			
		||||
 | 
			
		||||
@ -20,6 +20,7 @@
 | 
			
		||||
UNSTABLE. We have been adding helpers randomly to this module.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import six
 | 
			
		||||
import os
 | 
			
		||||
import time
 | 
			
		||||
import hashlib
 | 
			
		||||
@ -31,20 +32,26 @@ import logging
 | 
			
		||||
import atexit
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
_ = lambda msg: gettext.dgettext('sugar-toolkit-gtk3', msg)
 | 
			
		||||
def _(msg): return gettext.dgettext('sugar-toolkit-gtk3', msg)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def printable_hash(in_hash):
 | 
			
		||||
    """Convert binary hash data into printable characters."""
 | 
			
		||||
    printable = ""
 | 
			
		||||
    for char in in_hash:
 | 
			
		||||
        printable = printable + binascii.b2a_hex(char)
 | 
			
		||||
        if six.PY3:
 | 
			
		||||
            char = bytes([char])
 | 
			
		||||
            printable = printable + binascii.b2a_hex(char).decode()
 | 
			
		||||
        else:
 | 
			
		||||
            printable = printable + binascii.b2a_hex(char)
 | 
			
		||||
    return printable
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def sha_data(data):
 | 
			
		||||
    """sha1 hash some bytes."""
 | 
			
		||||
    sha_hash = hashlib.sha1()
 | 
			
		||||
    if six.PY3:
 | 
			
		||||
        data = data.encode('utf-8')
 | 
			
		||||
    sha_hash.update(data)
 | 
			
		||||
    return sha_hash.digest()
 | 
			
		||||
 | 
			
		||||
@ -81,7 +88,7 @@ def is_hex(s):
 | 
			
		||||
 | 
			
		||||
def validate_activity_id(actid):
 | 
			
		||||
    """Validate an activity ID."""
 | 
			
		||||
    if not isinstance(actid, (str, unicode)):
 | 
			
		||||
    if not isinstance(actid, (six.binary_type, six.text_type)):
 | 
			
		||||
        return False
 | 
			
		||||
    if len(actid) != ACTIVITY_ID_LEN:
 | 
			
		||||
        return False
 | 
			
		||||
@ -204,7 +211,7 @@ class LRU:
 | 
			
		||||
            yield j
 | 
			
		||||
 | 
			
		||||
    def keys(self):
 | 
			
		||||
        return self.d.keys()
 | 
			
		||||
        return list(self.d.keys())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
units = [['%d year', '%d years', 356 * 24 * 60 * 60],
 | 
			
		||||
@ -331,13 +338,14 @@ class TempFilePath(str):
 | 
			
		||||
 | 
			
		||||
def _cleanup_temp_files():
 | 
			
		||||
    logging.debug('_cleanup_temp_files')
 | 
			
		||||
    for path in _tracked_paths.keys():
 | 
			
		||||
    for path in list(_tracked_paths.keys()):
 | 
			
		||||
        try:
 | 
			
		||||
            os.unlink(path)
 | 
			
		||||
        except:
 | 
			
		||||
        except BaseException:
 | 
			
		||||
            # pylint: disable=W0702
 | 
			
		||||
            logging.exception('Exception occurred in _cleanup_temp_files')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
atexit.register(_cleanup_temp_files)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -2,6 +2,6 @@
 | 
			
		||||
name = Sample
 | 
			
		||||
activity_version = 1
 | 
			
		||||
bundle_id = org.sugarlabs.Sample
 | 
			
		||||
exec = sugar-activity activity.SampleActivity
 | 
			
		||||
exec = sugar-activity3 activity.SampleActivity
 | 
			
		||||
icon = activity-sample
 | 
			
		||||
license = GPLv2+
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
#!/usr/bin/env python2
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
 | 
			
		||||
from sugar3.activity import bundlebuilder
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -59,6 +59,7 @@ def timeout_cb():
 | 
			
		||||
        return False
 | 
			
		||||
    return True
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
GLib.timeout_add(50, timeout_cb)
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
#!/usr/bin/env python2
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
 | 
			
		||||
# Copyright (C) 2006, Red Hat, Inc.
 | 
			
		||||
# Copyright (C) 2007, One Laptop Per Child
 | 
			
		||||
 | 
			
		||||
@ -23,7 +23,7 @@ from sugar3.test import uitree
 | 
			
		||||
 | 
			
		||||
class TestUITree(unittest.TestCase):
 | 
			
		||||
    def test_tree(self):
 | 
			
		||||
        process = subprocess.Popen(["python", __file__, "show_window1"])
 | 
			
		||||
        process = subprocess.Popen(["python3", __file__, "show_window1"])
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            root = uitree.get_root()
 | 
			
		||||
@ -49,5 +49,6 @@ def show_window1():
 | 
			
		||||
 | 
			
		||||
    Gtk.main()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    globals()[sys.argv[1]]()
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user