diff --git a/src/sugar/activity/activity.py b/src/sugar/activity/activity.py index dee7f90f..df5d8f09 100644 --- a/src/sugar/activity/activity.py +++ b/src/sugar/activity/activity.py @@ -74,7 +74,9 @@ from sugar.session import XSMPClient from sugar import wm # support deprecated imports -from sugar.activity.widgets import ActivityToolbar, EditToolbar, ActivityToolbox +from sugar.activity.widgets import ActivityToolbar, EditToolbar +from sugar.activity.widgets import ActivityToolbox + _ = lambda msg: gettext.dgettext('sugar-toolkit', msg) @@ -86,17 +88,20 @@ J_DBUS_SERVICE = 'org.laptop.Journal' J_DBUS_PATH = '/org/laptop/Journal' J_DBUS_INTERFACE = 'org.laptop.Journal' + class _ActivitySession(gobject.GObject): + __gsignals__ = { 'quit-requested': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])), - 'quit': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])) + 'quit': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])), } def __init__(self): gobject.GObject.__init__(self) self._xsmp_client = XSMPClient() - self._xsmp_client.connect('quit-requested', self.__sm_quit_requested_cb) + self._xsmp_client.connect('quit-requested', + self.__sm_quit_requested_cb) self._xsmp_client.connect('quit', self.__sm_quit_cb) self._xsmp_client.startup() @@ -133,6 +138,7 @@ class _ActivitySession(gobject.GObject): def __sm_quit_cb(self, client): self.emit('quit') + class Activity(Window, gtk.Container): """This is the base Activity class that all other Activities derive from. This is where your activity starts. @@ -160,12 +166,12 @@ class Activity(Window, gtk.Container): 2. Implement read_file() and write_file() Most activities revolve around creating and storing Journal entries. - For example, Write: You create a document, it is saved to the Journal - and then later you resume working on the document. + For example, Write: You create a document, it is saved to the + Journal and then later you resume working on the document. read_file() and write_file() will be called by sugar to tell your - Activity that it should load or save the document the user is working - on. + Activity that it should load or save the document the user is + working on. 3. Implement our Activity Toolbars. The Toolbars are added to your Activity in step 1 (the toolbox), but @@ -177,9 +183,9 @@ class Activity(Window, gtk.Container): okay, but you should really stop and think about why not!) You do this with the ActivityToolbox(self) call in step 1. - Usually, you will also need the standard EditToolbar. This is the one - which has the standard copy and paste buttons. You need to derive - your own EditToolbar class from sugar.EditToolbar: + Usually, you will also need the standard EditToolbar. This is the + one which has the standard copy and paste buttons. You need to + derive your own EditToolbar class from sugar.EditToolbar: class EditToolbar(activity.EditToolbar): ... @@ -200,11 +206,12 @@ class Activity(Window, gtk.Container): Hint: A good and simple Activity to learn from is the Read activity. To create your own activity, you may want to copy it and use it as a template. """ + __gtype_name__ = 'SugarActivity' __gsignals__ = { 'shared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])), - 'joined': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])) + 'joined': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])), } def __init__(self, handle, create_jobject=True): @@ -368,8 +375,8 @@ class Activity(Window, gtk.Container): The activity id is sort-of-like the unix process id (PID). However, unlike PIDs it is only different for each new instance (with create_jobject = True set) and stays the same everytime a user - resumes an activity. This is also the identity of your Activity to other - XOs for use when sharing. + resumes an activity. This is also the identity of your Activity to + other XOs for use when sharing. """ return self._activity_id @@ -378,7 +385,8 @@ class Activity(Window, gtk.Container): return os.environ['SUGAR_BUNDLE_ID'] def set_canvas(self, canvas): - """Sets the 'work area' of your activity with the canvas of your choice. + """Sets the 'work area' of your activity with the canvas of your + choice. One commonly used canvas is gtk.ScrolledWindow """ @@ -425,8 +433,8 @@ class Activity(Window, gtk.Container): ~/.sugar/default/MyActivityName/ Activities should ONLY save settings, user preferences and other data - which isn't specific to a journal item here. If (meta-)data is in anyway - specific to a journal entry, it MUST be stored in the DataStore. + which isn't specific to a journal item here. If (meta-)data is in + anyway specific to a journal entry, it MUST be stored in the DataStore. """ if os.environ.has_key('SUGAR_ACTIVITY_ROOT') and \ os.environ['SUGAR_ACTIVITY_ROOT']: @@ -446,9 +454,10 @@ class Activity(Window, gtk.Container): close it. Although not required, this is also a good time to read all meta-data: - the file itself cannot be changed externally, but the title, description - and other metadata['tags'] may change. So if it is important for you to - notice changes, this is the time to record the originals. + the file itself cannot be changed externally, but the title, + description and other metadata['tags'] may change. So if it is + important for you to notice changes, this is the time to record the + originals. """ raise NotImplementedError @@ -461,12 +470,13 @@ class Activity(Window, gtk.Container): all document data to it. Additionally, you should also write any metadata needed to resume your - activity. For example, the Read activity saves the current page and zoom - level, so it can display the page. + activity. For example, the Read activity saves the current page and + zoom level, so it can display the page. Note: Currently, the file_path *WILL* be different from the one you - received in file_read(). Even if you kept the file_path from file_read() - open until now, you must still write the entire file to this file_path. + received in file_read(). Even if you kept the file_path from + file_read() open until now, you must still write the entire file to + this file_path. """ raise NotImplementedError @@ -518,6 +528,7 @@ class Activity(Window, gtk.Container): gtk.gdk.INTERP_BILINEAR) preview_data = [] + def save_func(buf, data): data.append(buf) @@ -809,8 +820,10 @@ class Activity(Window, gtk.Container): # DEPRECATED _shared_activity = property(lambda self: self.shared_activity, None) + _session = None + def _get_session(): global _session @@ -819,14 +832,17 @@ def _get_session(): return _session + def get_bundle_name(): """Return the bundle name for the current process' bundle""" return os.environ['SUGAR_BUNDLE_NAME'] + def get_bundle_path(): """Return the bundle path for the current process' bundle""" return os.environ['SUGAR_BUNDLE_PATH'] + def get_activity_root(): """Returns a path for saving Activity specific preferences, etc.""" if os.environ.has_key('SUGAR_ACTIVITY_ROOT') and \ @@ -835,6 +851,7 @@ def get_activity_root(): else: raise RuntimeError("No SUGAR_ACTIVITY_ROOT set.") + def show_object_in_journal(object_id): bus = dbus.SessionBus() obj = bus.get_object(J_DBUS_SERVICE, J_DBUS_PATH) diff --git a/src/sugar/activity/activityfactory.py b/src/sugar/activity/activityfactory.py index f2ca5ba9..ee0fd921 100644 --- a/src/sugar/activity/activityfactory.py +++ b/src/sugar/activity/activityfactory.py @@ -54,6 +54,8 @@ try: MAXFD = os.sysconf("SC_OPEN_MAX") except ValueError: MAXFD = 256 + + def _close_fds(): for i in xrange(3, MAXFD): try: @@ -62,6 +64,7 @@ def _close_fds(): except Exception: pass + def create_activity_id(): """Generate a new, unique ID for this activity""" pservice = presenceservice.get_instance() @@ -84,6 +87,7 @@ def create_activity_id(): return act_id raise RuntimeError("Cannot generate unique activity id.") + def get_environment(activity): environ = os.environ.copy() @@ -106,15 +110,17 @@ def get_environment(activity): os.mkdir(tmp_dir) environ['SUGAR_BUNDLE_PATH'] = activity.get_path() - environ['SUGAR_BUNDLE_ID'] = activity.get_bundle_id() + environ['SUGAR_BUNDLE_ID'] = activity.get_bundle_id() environ['SUGAR_ACTIVITY_ROOT'] = activity_root environ['PATH'] = bin_path + ':' + environ['PATH'] if activity.get_path().startswith(env.get_user_activities_path()): - environ['SUGAR_LOCALEDIR'] = os.path.join(activity.get_path(), 'locale') + environ['SUGAR_LOCALEDIR'] = os.path.join(activity.get_path(), + 'locale') return environ + def get_command(activity, activity_id=None, object_id=None, uri=None): if not activity_id: activity_id = create_activity_id() @@ -140,6 +146,7 @@ def get_command(activity, activity_id=None, object_id=None, uri=None): return command + def open_log_file(activity): i = 1 while True: @@ -154,10 +161,11 @@ def open_log_file(activity): i += 1 elif e.errno == ENOSPC: # not the end of the world; let's try to keep going. - return ('/dev/null', open('/dev/null','w')) + return ('/dev/null', open('/dev/null', 'w')) else: raise e + class ActivityCreationHandler(gobject.GObject): """Sugar-side activity creation interface @@ -250,7 +258,7 @@ class ActivityCreationHandler(gobject.GObject): '-u', pwd.getpwuid(os.getuid()).pw_name, '-i', environ['SUGAR_BUNDLE_ID'], '-e', environment_dir, - '--' + '--', ] + command for key, value in environ.items(): @@ -310,25 +318,29 @@ class ActivityCreationHandler(gobject.GObject): logging.error('Datastore find failed %s', err) self._launch_activity() + def create(bundle, activity_handle=None): """Create a new activity from its name.""" if not activity_handle: activity_handle = ActivityHandle() return ActivityCreationHandler(bundle, activity_handle) + def create_with_uri(bundle, uri): """Create a new activity and pass the uri as handle.""" activity_handle = ActivityHandle(uri=uri) return ActivityCreationHandler(bundle, activity_handle) + def create_with_object_id(bundle, object_id): """Create a new activity and pass the object id as handle.""" activity_handle = ActivityHandle(object_id=object_id) return ActivityCreationHandler(bundle, activity_handle) -# FIXME we use standalone method here instead of ActivityCreationHandler's -# member to have workaround code, see #1123 + def _child_watch_cb(pid, condition, user_data): + # FIXME we use standalone method here instead of ActivityCreationHandler's + # member to have workaround code, see #1123 environment_dir, log_file = user_data if environment_dir is not None: subprocess.call(['/bin/rm', '-rf', environment_dir]) diff --git a/src/sugar/activity/activityhandle.py b/src/sugar/activity/activityhandle.py index 7240e3c5..4ceadb00 100644 --- a/src/sugar/activity/activityhandle.py +++ b/src/sugar/activity/activityhandle.py @@ -19,11 +19,11 @@ STABLE. """ + class ActivityHandle(object): """Data structure storing simple activity metadata""" - def __init__( - self, activity_id=None, object_id=None, uri=None - ): + + def __init__(self, activity_id=None, object_id=None, uri=None): """Initialise the handle from activity_id activity_id -- unique id for the activity to be @@ -52,7 +52,7 @@ class ActivityHandle(object): def get_dict(self): """Retrieve our settings as a dictionary""" - result = { 'activity_id' : self.activity_id } + result = {'activity_id': self.activity_id} if self.object_id: result['object_id'] = self.object_id if self.uri: @@ -60,11 +60,10 @@ class ActivityHandle(object): return result + def create_from_dict(handle_dict): """Create a handle from a dictionary of parameters""" - result = ActivityHandle( - handle_dict['activity_id'], + result = ActivityHandle(handle_dict['activity_id'], object_id = handle_dict.get('object_id'), - uri = handle_dict.get('uri'), - ) + uri = handle_dict.get('uri')) return result diff --git a/src/sugar/activity/activityservice.py b/src/sugar/activity/activityservice.py index 1e5d2ea1..36f485c5 100644 --- a/src/sugar/activity/activityservice.py +++ b/src/sugar/activity/activityservice.py @@ -24,10 +24,12 @@ import logging import dbus import dbus.service + _ACTIVITY_SERVICE_NAME = "org.laptop.Activity" _ACTIVITY_SERVICE_PATH = "/org/laptop/Activity" _ACTIVITY_INTERFACE = "org.laptop.Activity" + class ActivityService(dbus.service.Object): """Base dbus service object that each Activity uses to export dbus methods. @@ -51,7 +53,7 @@ class ActivityService(dbus.service.Object): activity_id = activity.get_id() service_name = _ACTIVITY_SERVICE_NAME + activity_id - object_path = _ACTIVITY_SERVICE_PATH + "/" + activity_id + object_path = _ACTIVITY_SERVICE_PATH + '/' + activity_id bus = dbus.SessionBus() bus_name = dbus.service.BusName(service_name, bus=bus) @@ -79,4 +81,3 @@ class ActivityService(dbus.service.Object): self._activity.get_document_path(async_cb, async_err_cb) except Exception, e: async_err_cb(e) - diff --git a/src/sugar/activity/bundlebuilder.py b/src/sugar/activity/bundlebuilder.py index b11c7d06..868ca3dd 100644 --- a/src/sugar/activity/bundlebuilder.py +++ b/src/sugar/activity/bundlebuilder.py @@ -34,9 +34,11 @@ from fnmatch import fnmatch from sugar import env from sugar.bundle.activitybundle import ActivityBundle + IGNORE_DIRS = ['dist', '.git'] IGNORE_FILES = ['.gitignore', 'MANIFEST', '*.pyc', '*~', '*.bak', 'pseudo.po'] + def list_files(base_dir, ignore_dirs=None, ignore_files=None): result = [] @@ -58,7 +60,9 @@ def list_files(base_dir, ignore_dirs=None, ignore_files=None): return result + class Config(object): + def __init__(self, source_dir=None, dist_dir = None, dist_name = None): self.source_dir = source_dir or os.getcwd() self.dist_dir = dist_dir or os.path.join(self.source_dir, 'dist') @@ -80,7 +84,7 @@ class Config(object): self.version = bundle.get_activity_version() self.activity_name = bundle.get_name() self.bundle_id = bundle.get_bundle_id() - self.bundle_name = reduce(lambda x, y:x+y, self.activity_name.split()) + self.bundle_name = reduce(lambda x, y: x+y, self.activity_name.split()) self.bundle_root_dir = self.bundle_name + '.activity' self.tar_root_dir = '%s-%d' % (self.bundle_name, self.version) @@ -90,7 +94,9 @@ class Config(object): self.xo_name = '%s-%d.xo' % (self.bundle_name, self.version) self.tar_name = '%s-%d.tar.bz2' % (self.bundle_name, self.version) + class Builder(object): + def __init__(self, config): self.config = config @@ -167,7 +173,9 @@ class Builder(object): for line in manifest: f.write(line + "\n") + class Packager(object): + def __init__(self, config): self.config = config self.package_path = None @@ -175,7 +183,9 @@ class Packager(object): if not os.path.exists(self.config.dist_dir): os.mkdir(self.config.dist_dir) + class XOPackager(Packager): + def __init__(self, builder): Packager.__init__(self, builder.config) @@ -200,7 +210,9 @@ class XOPackager(Packager): bundle_zip.close() + class SourcePackager(Packager): + def __init__(self, config): Packager.__init__(self, config) self.package_path = os.path.join(self.config.dist_dir, @@ -210,7 +222,7 @@ class SourcePackager(Packager): git_ls = subprocess.Popen(['git', 'ls-files'], stdout=subprocess.PIPE, cwd=self.config.source_dir) stdout, _ = git_ls.communicate() - if git_ls.returncode : + if git_ls.returncode: # Fall back to filtered list return list_files(self.config.source_dir, IGNORE_DIRS, IGNORE_FILES) @@ -224,8 +236,9 @@ class SourcePackager(Packager): os.path.join(self.config.tar_root_dir, f)) tar.close() + class Installer(object): - IGNORES = [ 'po/*', 'MANIFEST', 'AUTHORS' ] + IGNORES = ['po/*', 'MANIFEST', 'AUTHORS'] def __init__(self, builder): self.config = builder.config @@ -261,6 +274,7 @@ class Installer(object): shutil.copy(source, dest) + def cmd_dev(config, args): '''Setup for development''' @@ -280,6 +294,7 @@ def cmd_dev(config, args): else: print 'ERROR - A bundle with the same name is already installed.' + def cmd_dist_xo(config, args): '''Create a xo bundle package''' @@ -290,6 +305,7 @@ def cmd_dist_xo(config, args): packager = XOPackager(Builder(config)) packager.package() + def cmd_fix_manifest(config, args): '''Add missing files to the manifest''' @@ -300,6 +316,7 @@ def cmd_fix_manifest(config, args): builder = Builder(config) builder.fix_manifest() + def cmd_dist_source(config, args): '''Create a tar source package''' @@ -310,6 +327,7 @@ def cmd_dist_source(config, args): packager = SourcePackager(config) packager.package() + def cmd_install(config, args): '''Install the activity in the system''' @@ -324,6 +342,7 @@ def cmd_install(config, args): installer = Installer(Builder(config)) installer.install(suboptions.prefix) + def cmd_genpot(config, args): '''Generate the gettext pot file''' @@ -354,14 +373,15 @@ def cmd_genpot(config, args): f.write('msgstr ""\n') f.close() - args = [ 'xgettext', '--join-existing', '--language=Python', - '--keyword=_', '--add-comments=TRANS:', '--output=%s' % pot_file ] + args = ['xgettext', '--join-existing', '--language=Python', + '--keyword=_', '--add-comments=TRANS:', '--output=%s' % pot_file] args += python_files retcode = subprocess.call(args) if retcode: print 'ERROR - xgettext failed with return code %i.' % retcode + def cmd_build(config, args): '''Build generated files''' @@ -372,6 +392,7 @@ def cmd_build(config, args): builder = Builder(config) builder.build() + def print_commands(): print 'Available commands:\n' @@ -382,6 +403,7 @@ def print_commands(): print '\n(Type "./setup.py --help" for help about a ' \ 'particular command\'s options.' + def start(bundle_name=None): if bundle_name: logging.warn("bundle_name deprecated, now comes from activity.info") @@ -397,5 +419,6 @@ def start(bundle_name=None): except (KeyError, IndexError): print_commands() + if __name__ == '__main__': start() diff --git a/src/sugar/activity/main.py b/src/sugar/activity/main.py index 168b2f4d..93f34e6f 100644 --- a/src/sugar/activity/main.py +++ b/src/sugar/activity/main.py @@ -30,17 +30,22 @@ from sugar.activity import activityhandle from sugar.bundle.activitybundle import ActivityBundle from sugar import logger + def create_activity_instance(constructor, handle): activity = constructor(handle) activity.show() + 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 @@ -54,6 +59,7 @@ class SingleProcess(dbus.service.Object): handle = activityhandle.create_from_dict(handle_dict) create_activity_instance(self.constructor, handle) + def main(): parser = OptionParser() parser.add_option("-b", "--bundle-id", dest="bundle_id", diff --git a/src/sugar/activity/namingalert.py b/src/sugar/activity/namingalert.py index 18e84a79..72db8dce 100644 --- a/src/sugar/activity/namingalert.py +++ b/src/sugar/activity/namingalert.py @@ -35,8 +35,10 @@ from sugar.graphics.canvastextview import CanvasTextView from sugar.bundle.activitybundle import ActivityBundle + _ = lambda msg: gettext.dgettext('sugar-toolkit', msg) + def _get_icon_name(metadata): file_name = None @@ -53,16 +55,17 @@ def _get_icon_name(metadata): return file_name + class NamingToolbar(gtk.Toolbar): """ Toolbar of the naming alert """ + __gtype_name__ = 'SugarNamingToolbar' __gsignals__ = { - 'keep-clicked': (gobject.SIGNAL_RUN_FIRST, - gobject.TYPE_NONE, - ([])) + 'keep-clicked': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])), } + def __init__(self): gtk.Toolbar.__init__(self) @@ -110,7 +113,9 @@ class NamingToolbar(gtk.Toolbar): def __keep_button_clicked_cb(self, widget, data=None): self.emit('keep-clicked') + class FavoriteIcon(CanvasIcon): + def __init__(self, favorite): CanvasIcon.__init__(self, icon_name='emblem-favorite', box_width=style.GRID_CELL_SIZE * 3 / 5, @@ -149,7 +154,9 @@ class FavoriteIcon(CanvasIcon): elif event.detail == hippo.MOTION_DETAIL_LEAVE: icon.props.fill_color = style.COLOR_TRANSPARENT.get_svg() + class NamingAlert(gtk.Window): + __gtype_name__ = 'SugarNamingAlert' def __init__(self, activity, bundle_path): @@ -212,11 +219,12 @@ class NamingAlert(gtk.Window): spacing=style.DEFAULT_SPACING) body.append(header) - descriptions = hippo.CanvasBox(orientation=hippo.ORIENTATION_HORIZONTAL, - spacing=style.DEFAULT_SPACING * 3, - padding_left=style.GRID_CELL_SIZE, - padding_right=style.GRID_CELL_SIZE, - padding_top=style.DEFAULT_SPACING * 3) + descriptions = hippo.CanvasBox( + orientation=hippo.ORIENTATION_HORIZONTAL, + spacing=style.DEFAULT_SPACING * 3, + padding_left=style.GRID_CELL_SIZE, + padding_right=style.GRID_CELL_SIZE, + padding_top=style.DEFAULT_SPACING * 3) body.append(descriptions, hippo.PACK_EXPAND) diff --git a/src/sugar/activity/widgets.py b/src/sugar/activity/widgets.py index 2a8b1a21..6ecdda3c 100644 --- a/src/sugar/activity/widgets.py +++ b/src/sugar/activity/widgets.py @@ -29,6 +29,7 @@ from sugar.graphics.xocolor import XoColor from sugar.graphics.icon import Icon from sugar.bundle.activitybundle import ActivityBundle + _ = lambda msg: gettext.dgettext('sugar-toolkit', msg) @@ -41,7 +42,9 @@ def _create_activity_icon(): icon = Icon(file=bundle.get_icon(), xo_color=color) return icon + class ActivityButton(ToolButton): + def __init__(self, activity, **kwargs): ToolButton.__init__(self, **kwargs) @@ -55,7 +58,9 @@ class ActivityButton(ToolButton): def __jobject_updated_cb(self, jobject): self.props.tooltip = jobject['title'] + class ActivityToolbarButton(ToolbarButton): + def __init__(self, activity, **kwargs): toolbar = ActivityToolbar(activity, orientation_left=True) toolbar.stop.hide() @@ -66,7 +71,9 @@ class ActivityToolbarButton(ToolbarButton): self.set_icon_widget(icon) icon.show() + class StopButton(ToolButton): + def __init__(self, activity, **kwargs): ToolButton.__init__(self, 'activity-stop', **kwargs) self.props.tooltip = _('Stop') @@ -76,28 +83,38 @@ class StopButton(ToolButton): def __stop_button_clicked_cb(self, button, activity): activity.close() + class UndoButton(ToolButton): + def __init__(self, **kwargs): ToolButton.__init__(self, 'edit-undo', **kwargs) self.props.tooltip = _('Undo') self.props.accelerator = 'Q' + class RedoButton(ToolButton): + def __init__(self, **kwargs): ToolButton.__init__(self, 'edit-redo', **kwargs) self.props.tooltip = _('Redo') + class CopyButton(ToolButton): + def __init__(self, **kwargs): ToolButton.__init__(self, 'edit-copy', **kwargs) self.props.tooltip = _('Copy') + class PasteButton(ToolButton): + def __init__(self, **kwargs): ToolButton.__init__(self, 'edit-paste', **kwargs) self.props.tooltip = _('Paste') + class ShareButton(RadioMenuButton): + def __init__(self, activity, **kwargs): palette = RadioPalette() @@ -137,7 +154,9 @@ class ShareButton(RadioMenuButton): finally: self.neighborhood.handler_unblock(self._neighborhood_handle) + class KeepButton(ToolButton): + def __init__(self, activity, **kwargs): ToolButton.__init__(self, **kwargs) self.props.tooltip = _('Keep') @@ -154,7 +173,9 @@ class KeepButton(ToolButton): def __keep_button_clicked_cb(self, button, activity): activity.copy() + class TitleEntry(gtk.ToolItem): + def __init__(self, activity, **kwargs): gtk.ToolItem.__init__(self) self.set_expand(False) @@ -195,6 +216,7 @@ class TitleEntry(gtk.ToolItem): self._update_title_sid = None return False + class ActivityToolbar(gtk.Toolbar): """The Activity toolbar with the Journal entry title, sharing, Keep and Stop buttons @@ -202,6 +224,7 @@ class ActivityToolbar(gtk.Toolbar): All activities should have this toolbar. It is easiest to add it to your Activity by using the ActivityToolbox. """ + def __init__(self, activity, orientation_left=False): gtk.Toolbar.__init__(self) @@ -232,6 +255,7 @@ class ActivityToolbar(gtk.Toolbar): self.insert(self.stop, -1) self.stop.show() + class EditToolbar(gtk.Toolbar): """Provides the standard edit toolbar for Activities. @@ -265,6 +289,7 @@ class EditToolbar(gtk.Toolbar): # And make it visible: self._edit_toolbar.show() """ + def __init__(self): gtk.Toolbar.__init__(self) @@ -289,6 +314,7 @@ class EditToolbar(gtk.Toolbar): self.insert(self.paste, -1) self.paste.show() + class ActivityToolbox(Toolbox): """Creates the Toolbox for the Activity @@ -307,6 +333,7 @@ class ActivityToolbox(Toolbox): # And make it visible: toolbox.show() """ + def __init__(self, activity): Toolbox.__init__(self) diff --git a/src/sugar/bundle/activitybundle.py b/src/sugar/bundle/activitybundle.py index 7600847c..36e37647 100644 --- a/src/sugar/bundle/activitybundle.py +++ b/src/sugar/bundle/activitybundle.py @@ -31,6 +31,7 @@ from sugar import util from sugar.bundle.bundle import Bundle, \ MalformedBundleException, NotInstalledException + class ActivityBundle(Bundle): """A Sugar activity bundle @@ -89,8 +90,8 @@ class ActivityBundle(Bundle): return ret def _read_manifest(self): - """return a list with the lines in MANIFEST, with invalid lines replaced - by empty lines. + """return a list with the lines in MANIFEST, with invalid lines + replaced by empty lines. Since absolute order carries information on file history, it should be preserved. For instance, when renaming a file, you should leave @@ -170,7 +171,7 @@ class ActivityBundle(Bundle): if cp.has_option(section, 'mime_types'): mime_list = cp.get(section, 'mime_types').strip(';') - self._mime_types = [ mime.strip() for mime in mime_list.split(';') ] + self._mime_types = [mime.strip() for mime in mime_list.split(';')] if cp.has_option(section, 'show_launcher'): if cp.get(section, 'show_launcher') == 'no': @@ -251,10 +252,10 @@ class ActivityBundle(Bundle): """Get the activity bundle id""" return self._bundle_id - # FIXME: this should return the icon data, not a filename, so that - # we don't need to create a temp file in the zip case def get_icon(self): """Get the activity icon name""" + # FIXME: this should return the icon data, not a filename, so that + # we don't need to create a temp file in the zip case icon_path = os.path.join('activity', self._icon + '.svg') if self._zip_file is None: return os.path.join(self._path, icon_path) @@ -301,7 +302,7 @@ class ActivityBundle(Bundle): # List installed files manifestfiles = self.get_files(self._raw_manifest()) - paths = [] + paths = [] for root, dirs_, files in os.walk(install_path): rel_path = root[len(install_path) + 1:] for f in files: @@ -320,7 +321,7 @@ class ActivityBundle(Bundle): # Is anything in MANIFEST left over after accounting for all files? if manifestfiles: err = ("Bundle %s: files in MANIFEST not included: %s"% - (self._name,str(manifestfiles))) + (self._name, str(manifestfiles))) if strict_manifest: raise MalformedBundleException(err) else: @@ -351,7 +352,7 @@ class ActivityBundle(Bundle): mime_types = self.get_mime_types() if mime_types is not None: installed_icons_dir = os.path.join(xdg_data_home, - 'icons/sugar/scalable/mimetypes') + 'icons/sugar/scalable/mimetypes') if not os.path.isdir(installed_icons_dir): os.makedirs(installed_icons_dir) @@ -390,7 +391,7 @@ class ActivityBundle(Bundle): mime_types = self.get_mime_types() if mime_types is not None: installed_icons_dir = os.path.join(xdg_data_home, - 'icons/sugar/scalable/mimetypes') + 'icons/sugar/scalable/mimetypes') if os.path.isdir(installed_icons_dir): for f in os.listdir(installed_icons_dir): path = os.path.join(installed_icons_dir, f) diff --git a/src/sugar/bundle/bundle.py b/src/sugar/bundle/bundle.py index 44037e88..c9763a00 100644 --- a/src/sugar/bundle/bundle.py +++ b/src/sugar/bundle/bundle.py @@ -26,24 +26,31 @@ import shutil import StringIO import zipfile + class AlreadyInstalledException(Exception): pass + class NotInstalledException(Exception): pass + class InvalidPathException(Exception): pass + class ZipExtractException(Exception): pass + class RegistrationException(Exception): pass + class MalformedBundleException(Exception): pass + class Bundle(object): """A Sugar activity, content module, etc. @@ -71,7 +78,7 @@ class Bundle(object): # manifest = self._get_file(self._infodir + '/contents') # if manifest is None: # raise MalformedBundleException('No manifest file') - # + # signature = self._get_file(self._infodir + '/contents.sig') # if signature is None: # raise MalformedBundleException('No signature file') @@ -112,7 +119,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, "rb") except IOError: return None else: diff --git a/src/sugar/bundle/contentbundle.py b/src/sugar/bundle/contentbundle.py index 2d19417a..4b483cbd 100644 --- a/src/sugar/bundle/contentbundle.py +++ b/src/sugar/bundle/contentbundle.py @@ -29,6 +29,7 @@ from sugar import env from sugar.bundle.bundle import Bundle, NotInstalledException, \ MalformedBundleException + class ContentBundle(Bundle): """A Sugar content bundle @@ -78,8 +79,8 @@ class ContentBundle(Bundle): try: if int(version) != 1: raise MalformedBundleException( - 'Content bundle %s has unknown host_version number %s' % - (self._path, version)) + 'Content bundle %s has unknown host_version ' + 'number %s' % (self._path, version)) except ValueError: raise MalformedBundleException( 'Content bundle %s has invalid host_version number %s' % @@ -209,17 +210,17 @@ class ContentBundle(Bundle): def get_start_uri(self): return "file://" + urllib.pathname2url(self.get_start_path()) - # TODO treat ContentBundle in special way - # needs rethinking while fixing ContentBundle support def get_bundle_id(self): + # TODO treat ContentBundle in special way + # needs rethinking while fixing ContentBundle support if self._bundle_class is not None: return self._bundle_class else: return self._global_name - # TODO treat ContentBundle in special way - # needs rethinking while fixing ContentBundle support def get_activity_version(self): + # TODO treat ContentBundle in special way + # needs rethinking while fixing ContentBundle support return self._library_version def is_installed(self): diff --git a/src/sugar/datastore/datastore.py b/src/sugar/datastore/datastore.py index 39dd8a68..f001d76c 100644 --- a/src/sugar/datastore/datastore.py +++ b/src/sugar/datastore/datastore.py @@ -29,10 +29,10 @@ import gobject from sugar.datastore import dbus_helpers from sugar import mime + class DSMetadata(gobject.GObject): __gsignals__ = { - 'updated': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([])) + 'updated': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])), } def __init__(self, props=None): @@ -80,7 +80,9 @@ class DSMetadata(gobject.GObject): else: return default + class DSObject(object): + def __init__(self, object_id, metadata=None, file_path=None): self.object_id = object_id self._metadata = metadata @@ -129,13 +131,14 @@ class DSObject(object): def __del__(self): if not self._destroyed: - logging.warning('DSObject was deleted without cleaning up first. ' \ + logging.warning('DSObject was deleted without cleaning up first. ' 'Call DSObject.destroy() before disposing it.') self.destroy() def copy(self): return DSObject(None, self._metadata.copy(), self._file_path) + def get(object_id): logging.debug('datastore.get') metadata = dbus_helpers.get_properties(object_id) @@ -144,12 +147,14 @@ def get(object_id): # TODO: register the object for updates return ds_object + def create(): metadata = DSMetadata() metadata['mtime'] = datetime.now().isoformat() metadata['timestamp'] = int(time.time()) return DSObject(object_id=None, metadata=metadata, file_path=None) + def write(ds_object, update_mtime=True, transfer_ownership=False, reply_handler=None, error_handler=None, timeout=-1): logging.debug('datastore.write') @@ -185,10 +190,12 @@ def write(ds_object, update_mtime=True, transfer_ownership=False, # TODO: register the object for updates logging.debug('Written object %s to the datastore.', ds_object.object_id) + def delete(object_id): logging.debug('datastore.delete') dbus_helpers.delete(object_id) + def find(query, sorting=None, limit=None, offset=None, properties=None, reply_handler=None, error_handler=None): @@ -217,6 +224,7 @@ def find(query, sorting=None, limit=None, offset=None, properties=None, return objects, total_count + def copy(jobject, mount_point): new_jobject = jobject.copy() @@ -238,17 +246,22 @@ def copy(jobject, mount_point): write(new_jobject) + def mount(uri, options, timeout=-1): return dbus_helpers.mount(uri, options, timeout=timeout) + def unmount(mount_point_id): dbus_helpers.unmount(mount_point_id) + def mounts(): return dbus_helpers.mounts() + def complete_indexing(): return dbus_helpers.complete_indexing() + def get_unique_values(key): return dbus_helpers.get_unique_values(key) diff --git a/src/sugar/datastore/dbus_helpers.py b/src/sugar/datastore/dbus_helpers.py index 37223fe5..008b7a0d 100644 --- a/src/sugar/datastore/dbus_helpers.py +++ b/src/sugar/datastore/dbus_helpers.py @@ -31,6 +31,7 @@ DS_DBUS_PATH = "/org/laptop/sugar/DataStore" _data_store = None + def _get_data_store(): global _data_store @@ -41,12 +42,14 @@ def _get_data_store(): DS_DBUS_INTERFACE) return _data_store + def create(properties, filename, transfer_ownership=False): object_id = _get_data_store().create(dbus.Dictionary(properties), filename, transfer_ownership) logging.debug('dbus_helpers.create: ' + object_id) return object_id + def update(uid, properties, filename, transfer_ownership=False, reply_handler=None, error_handler=None, timeout=-1): debug_props = properties.copy() @@ -64,19 +67,23 @@ def update(uid, properties, filename, transfer_ownership=False, _get_data_store().update(uid, dbus.Dictionary(properties), filename, transfer_ownership) + def delete(uid): logging.debug('dbus_helpers.delete: %r', uid) _get_data_store().delete(uid) + def get_properties(uid): logging.debug('dbus_helpers.get_properties: %s', uid) return _get_data_store().get_properties(uid, byte_arrays=True) + def get_filename(uid): filename = _get_data_store().get_filename(uid) logging.debug('dbus_helpers.get_filename: %s, %s', uid, filename) return filename + def find(query, properties, reply_handler, error_handler): logging.debug('dbus_helpers.find: %r %r', query, properties) if reply_handler and error_handler: @@ -86,19 +93,23 @@ def find(query, properties, reply_handler, error_handler): else: return _get_data_store().find(query, properties, byte_arrays=True) + def mount(uri, options, timeout=-1): return _get_data_store().mount(uri, options, timeout=timeout) + def unmount(mount_point_id): _get_data_store().unmount(mount_point_id) + def mounts(): return _get_data_store().mounts() + def get_unique_values(key): return _get_data_store().get_uniquevaluesfor( key, dbus.Dictionary({}, signature='ss')) + def complete_indexing(): return _get_data_store().complete_indexing() - diff --git a/src/sugar/env.py b/src/sugar/env.py index 91e91d3f..655d18da 100644 --- a/src/sugar/env.py +++ b/src/sugar/env.py @@ -22,12 +22,14 @@ STABLE. import os + def is_emulator(): if os.environ.has_key('SUGAR_EMULATOR'): if os.environ['SUGAR_EMULATOR'] == 'yes': return True return False + def get_profile_path(path=None): if os.environ.has_key('SUGAR_PROFILE'): profile_id = os.environ['SUGAR_PROFILE'] @@ -46,6 +48,7 @@ def get_profile_path(path=None): else: return base + def get_logs_path(path=None): base = os.environ.get('SUGAR_LOGS_DIR', get_profile_path('logs')) if path != None: @@ -53,8 +56,10 @@ def get_logs_path(path=None): else: return base + def get_user_activities_path(): return os.path.expanduser('~/Activities') + def get_user_library_path(): return os.path.expanduser('~/Library') diff --git a/src/sugar/graphics/alert.py b/src/sugar/graphics/alert.py index b108d9dd..4441909e 100644 --- a/src/sugar/graphics/alert.py +++ b/src/sugar/graphics/alert.py @@ -54,8 +54,10 @@ import math from sugar.graphics import style from sugar.graphics.icon import Icon + _ = lambda msg: gettext.dgettext('sugar-toolkit', msg) + class Alert(gtk.EventBox): """ UI interface for Alerts @@ -78,18 +80,14 @@ class Alert(gtk.EventBox): __gtype_name__ = 'SugarAlert' __gsignals__ = { - 'response': (gobject.SIGNAL_RUN_FIRST, - gobject.TYPE_NONE, ([object])) - } + 'response': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([object])), + } __gproperties__ = { - 'title' : (str, None, None, None, - gobject.PARAM_READWRITE), - 'msg' : (str, None, None, None, - gobject.PARAM_READWRITE), - 'icon' : (object, None, None, - gobject.PARAM_WRITABLE) - } + 'title': (str, None, None, None, gobject.PARAM_READWRITE), + 'msg': (str, None, None, None, gobject.PARAM_READWRITE), + 'icon': (object, None, None, gobject.PARAM_WRITABLE), + } def __init__(self, **kwargs): diff --git a/src/sugar/graphics/animator.py b/src/sugar/graphics/animator.py index 51c8ac34..8fb298b7 100644 --- a/src/sugar/graphics/animator.py +++ b/src/sugar/graphics/animator.py @@ -24,12 +24,13 @@ import time import gobject EASE_OUT_EXPO = 0 -EASE_IN_EXPO = 1 +EASE_IN_EXPO = 1 + class Animator(gobject.GObject): + __gsignals__ = { - 'completed': (gobject.SIGNAL_RUN_FIRST, - gobject.TYPE_NONE, ([])), + 'completed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])), } def __init__(self, duration, fps=20, easing=EASE_OUT_EXPO): @@ -111,7 +112,9 @@ class Animator(gobject.GObject): else: return True + class Animation(object): + def __init__(self, start, end): self.start = start self.end = end diff --git a/src/sugar/graphics/canvastextview.py b/src/sugar/graphics/canvastextview.py index 4a2aaa49..853af9f9 100644 --- a/src/sugar/graphics/canvastextview.py +++ b/src/sugar/graphics/canvastextview.py @@ -20,7 +20,9 @@ import hippo from sugar.graphics import style + class CanvasTextView(hippo.CanvasWidget): + def __init__(self, text, **kwargs): hippo.CanvasWidget.__init__(self, **kwargs) self.text_view_widget = gtk.TextView() diff --git a/src/sugar/graphics/colorbutton.py b/src/sugar/graphics/colorbutton.py index f69691cc..1fed96d7 100644 --- a/src/sugar/graphics/colorbutton.py +++ b/src/sugar/graphics/colorbutton.py @@ -26,12 +26,15 @@ from sugar.graphics import style from sugar.graphics.icon import Icon from sugar.graphics.palette import Palette, ToolInvoker, WidgetInvoker + _ = lambda msg: gettext.dgettext('sugar-toolkit', msg) + def get_svg_color_string(color): return '#%.2X%.2X%.2X' % (color.red / 257, color.green / 257, color.blue / 257) + class _ColorButton(gtk.Button): """This is a ColorButton for Sugar. It is similar to the gtk.ColorButton, but does not have any alpha support. @@ -42,8 +45,8 @@ class _ColorButton(gtk.Button): """ __gtype_name__ = 'SugarColorButton' - __gsignals__ = { 'color-set' : (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - tuple())} + __gsignals__ = {'color-set': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + tuple())} def __init__(self, **kwargs): self._title = _('Choose a color') @@ -78,7 +81,7 @@ class _ColorButton(gtk.Button): if self._has_palette and self._has_invoker: self._invoker = WidgetInvoker(self) # FIXME: This is a hack. - self._invoker.has_rectangle_gap = lambda : False + self._invoker.has_rectangle_gap = lambda: False self._invoker.palette = self._palette def create_palette(self): @@ -86,7 +89,8 @@ class _ColorButton(gtk.Button): self._palette = _ColorPalette(color=self._color, primary_text=self._title) self._palette.connect('color-set', self.__palette_color_set_cb) - self._palette.connect('notify::color', self.__palette_color_changed) + self._palette.connect('notify::color', self. + __palette_color_changed) return self._palette @@ -192,8 +196,8 @@ class _ColorButton(gtk.Button): getter=_get_accept_drag, setter=_set_accept_drag) - # Drag and Drop def __drag_begin_cb(self, widget, context): + # Drag and Drop pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, True, 8, style.SMALL_ICON_SIZE, style.SMALL_ICON_SIZE) @@ -235,10 +239,11 @@ class _ColorPalette(Palette): _BLUE = 2 __gtype_name__ = 'SugarColorPalette' + # The color-set signal is emitted when the user is finished selecting # a color. - __gsignals__ = { 'color-set' : (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - tuple())} + __gsignals__ = {'color-set': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + tuple())} def __init__(self, **kwargs): self._color = gtk.gdk.Color(0, 0, 0) @@ -300,8 +305,6 @@ class _ColorPalette(Palette): return scale - - def _build_swatches(self): for child in self._swatch_tray.get_children(): child.destroy() @@ -384,7 +387,6 @@ class _ColorPalette(Palette): color = gobject.property(type=object, getter=get_color, setter=set_color) - def _add_accelerator(tool_button): if not tool_button.props.accelerator or not tool_button.get_toplevel() or \ not tool_button.child: @@ -403,20 +405,24 @@ def _add_accelerator(tool_button): tool_button.child.add_accelerator('clicked', accel_group, keyval, mask, gtk.ACCEL_LOCKED | gtk.ACCEL_VISIBLE) + def _hierarchy_changed_cb(tool_button, previous_toplevel): _add_accelerator(tool_button) + def setup_accelerator(tool_button): _add_accelerator(tool_button) tool_button.connect('hierarchy-changed', _hierarchy_changed_cb) -# This not ideal. It would be better to subclass gtk.ToolButton, however -# the python bindings do not seem to be powerfull enough for that. -# (As we need to change a variable in the class structure.) + class ColorToolButton(gtk.ToolItem): + # This not ideal. It would be better to subclass gtk.ToolButton, however + # the python bindings do not seem to be powerfull enough for that. + # (As we need to change a variable in the class structure.) + __gtype_name__ = 'SugarColorToolButton' - __gsignals__ = { 'color-set' : (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - tuple())} + __gsignals__ = {'color-set': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + tuple())} def __init__(self, icon_name='color-preview', **kwargs): self._accelerator = None @@ -528,4 +534,3 @@ class ColorToolButton(gtk.ToolItem): def __color_set_cb(self, widget): self.emit('color-set') - diff --git a/src/sugar/graphics/combobox.py b/src/sugar/graphics/combobox.py index fe18087b..bc759c23 100644 --- a/src/sugar/graphics/combobox.py +++ b/src/sugar/graphics/combobox.py @@ -22,7 +22,9 @@ STABLE. import gobject import gtk + class ComboBox(gtk.ComboBox): + __gtype_name__ = 'SugarComboBox' def __init__(self): diff --git a/src/sugar/graphics/entry.py b/src/sugar/graphics/entry.py index 62975daa..6afafa01 100644 --- a/src/sugar/graphics/entry.py +++ b/src/sugar/graphics/entry.py @@ -22,7 +22,9 @@ STABLE. import gtk import hippo + class CanvasEntry(hippo.CanvasEntry): + def set_background(self, color_spec): """ Parameters diff --git a/src/sugar/graphics/icon.py b/src/sugar/graphics/icon.py index cc6c8d2f..d13e0791 100644 --- a/src/sugar/graphics/icon.py +++ b/src/sugar/graphics/icon.py @@ -33,9 +33,12 @@ import cairo from sugar.graphics.xocolor import XoColor from sugar.util import LRU + _BADGE_SIZE = 0.45 + class _SVGLoader(object): + def __init__(self): self._cache = LRU(50) @@ -61,20 +64,26 @@ class _SVGLoader(object): import rsvg # XXX this is very slow! why? return rsvg.Handle(data=icon) + class _IconInfo(object): + def __init__(self): self.file_name = None self.attach_x = 0 self.attach_y = 0 + class _BadgeInfo(object): + def __init__(self): self.attach_x = 0 self.attach_y = 0 self.size = 0 self.icon_padding = 0 + class _IconBuffer(object): + _surface_cache = LRU(50) _loader = _SVGLoader() @@ -219,7 +228,7 @@ class _IconBuffer(object): self.stroke_color = None self.fill_color = None - def _get_insensitive_pixbuf (self, pixbuf, widget): + def _get_insensitive_pixbuf(self, pixbuf, widget): if not (widget and widget.style): return pixbuf @@ -309,7 +318,9 @@ class _IconBuffer(object): xo_color = property(_get_xo_color, _set_xo_color) + class Icon(gtk.Image): + __gtype_name__ = 'SugarIcon' def __init__(self, **kwargs): @@ -510,7 +521,9 @@ class Icon(gtk.Image): badge_name = gobject.property( type=str, getter=get_badge_name, setter=set_badge_name) + class CanvasIcon(hippo.CanvasBox, hippo.CanvasItem): + __gtype_name__ = 'CanvasIcon' def __init__(self, **kwargs): @@ -932,11 +945,13 @@ class CanvasIcon(hippo.CanvasBox, hippo.CanvasItem): palette = property(get_palette, set_palette) + class CellRendererIcon(gtk.GenericCellRenderer): + __gtype_name__ = 'SugarCellRendererIcon' __gsignals__ = { - 'clicked': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, [object]) + 'clicked': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, [object]), } def __init__(self, tree_view): @@ -1073,8 +1088,8 @@ class CellRendererIcon(gtk.GenericCellRenderer): return False - def on_render(self, window, widget, background_area, cell_area, expose_area, - flags): + def on_render(self, window, widget, background_area, cell_area, + expose_area, flags): has_prelit_colors = None not in [self._prelit_fill_color, self._prelit_stroke_color] if flags & gtk.CELL_RENDERER_PRELIT and has_prelit_colors and \ @@ -1100,6 +1115,7 @@ class CellRendererIcon(gtk.GenericCellRenderer): cr.rectangle(expose_area) cr.paint() + def get_icon_state(base_name, perc, step=5): strength = round(perc / step) * step icon_theme = gtk.icon_theme_get_default() @@ -1111,6 +1127,7 @@ def get_icon_state(base_name, perc, step=5): strength = strength + step + def get_icon_file_name(icon_name): icon_theme = gtk.icon_theme_get_default() info = icon_theme.lookup_icon(icon_name, gtk.ICON_SIZE_LARGE_TOOLBAR, 0) @@ -1119,4 +1136,3 @@ def get_icon_file_name(icon_name): filename = info.get_filename() del info return filename - diff --git a/src/sugar/graphics/iconentry.py b/src/sugar/graphics/iconentry.py index df38b9e3..4ce2c4fd 100644 --- a/src/sugar/graphics/iconentry.py +++ b/src/sugar/graphics/iconentry.py @@ -25,6 +25,7 @@ from sugar.graphics.icon import _SVGLoader ICON_ENTRY_PRIMARY = _sugarext.ICON_ENTRY_PRIMARY ICON_ENTRY_SECONDARY = _sugarext.ICON_ENTRY_SECONDARY + class IconEntry(_sugarext.IconEntry): def __init__(self): @@ -103,4 +104,3 @@ class IconEntry(_sugarext.IconEntry): self.hide_clear_button() else: self.show_clear_button() - diff --git a/src/sugar/graphics/menuitem.py b/src/sugar/graphics/menuitem.py index 7af8a6a8..655f7cc7 100644 --- a/src/sugar/graphics/menuitem.py +++ b/src/sugar/graphics/menuitem.py @@ -27,7 +27,9 @@ import gtk from sugar.graphics.icon import Icon + class MenuItem(gtk.ImageMenuItem): + def __init__(self, text_label=None, icon_name=None, text_maxlen=60, xo_color=None, file_name=None): gobject.GObject.__init__(self) @@ -91,4 +93,3 @@ class MenuItem(gtk.ImageMenuItem): accelerator = gobject.property(type=str, setter=set_accelerator, getter=get_accelerator) - diff --git a/src/sugar/graphics/notebook.py b/src/sugar/graphics/notebook.py index 65d35224..603b7c9a 100644 --- a/src/sugar/graphics/notebook.py +++ b/src/sugar/graphics/notebook.py @@ -27,13 +27,14 @@ STABLE. import gtk import gobject + class Notebook(gtk.Notebook): + __gtype_name__ = 'SugarNotebook' __gproperties__ = { 'can-close-tabs': (bool, None, None, False, - gobject.PARAM_READWRITE | - gobject.PARAM_CONSTRUCT_ONLY) + gobject.PARAM_READWRITE | gobject.PARAM_CONSTRUCT_ONLY), } def __init__(self, **kwargs): diff --git a/src/sugar/graphics/objectchooser.py b/src/sugar/graphics/objectchooser.py index 582ac62e..590e35d5 100644 --- a/src/sugar/graphics/objectchooser.py +++ b/src/sugar/graphics/objectchooser.py @@ -27,11 +27,14 @@ import dbus from sugar.datastore import datastore + J_DBUS_SERVICE = 'org.laptop.Journal' J_DBUS_INTERFACE = 'org.laptop.Journal' J_DBUS_PATH = '/org/laptop/Journal' + class ObjectChooser(object): + def __init__(self, title=None, parent=None, flags=None, buttons=None, what_filter=None): # For backwards compatibility: @@ -127,4 +130,3 @@ class ObjectChooser(object): # Journal service disappeared from the bus self._response_code = gtk.RESPONSE_CANCEL self._cleanup() - diff --git a/src/sugar/graphics/palette.py b/src/sugar/graphics/palette.py index f6133060..5db0bd2a 100644 --- a/src/sugar/graphics/palette.py +++ b/src/sugar/graphics/palette.py @@ -39,16 +39,18 @@ from sugar import _sugarext from sugar.graphics.palettewindow import MouseSpeedDetector, Invoker, \ WidgetInvoker, CanvasInvoker, ToolInvoker, CellRendererInvoker + class Palette(PaletteWindow): PRIMARY = 0 SECONDARY = 1 __gtype_name__ = 'SugarPalette' - # DEPRECATED: label is passed with the primary-text property, accel_path - # is set via the invoker property, and menu_after_content is not used def __init__(self, label=None, accel_path=None, menu_after_content=False, text_maxlen=60, **kwargs): + # DEPRECATED: label is passed with the primary-text property, + # accel_path is set via the invoker property, and menu_after_content + # is not used self._primary_text = None self._secondary_text = None @@ -238,10 +240,9 @@ class Palette(PaletteWindow): def get_secondary_text(self): return self._secondary_text + secondary_text = gobject.property(type=str, getter=get_secondary_text, + setter=set_secondary_text) - secondary_text = gobject.property(type=str, - getter=get_secondary_text, - setter=set_secondary_text) def _show_icon(self): self._label_alignment.set_padding(0, 0, 0, style.DEFAULT_SPACING) self._icon_box.show() @@ -362,7 +363,9 @@ class Palette(PaletteWindow): self._palette_state = state + class PaletteActionBar(gtk.HButtonBox): + def add_action(self, label, icon_name=None): button = gtk.Button(label) @@ -374,11 +377,13 @@ class PaletteActionBar(gtk.HButtonBox): self.pack_start(button) button.show() + class _Menu(_sugarext.Menu): + __gtype_name__ = 'SugarPaletteMenu' __gsignals__ = { - 'item-inserted': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])) + 'item-inserted': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])), } def __init__(self, palette): @@ -410,7 +415,9 @@ class _Menu(_sugarext.Menu): def do_deactivate(self): self._palette.hide() + class _SecondaryAnimation(animator.Animation): + def __init__(self, palette): animator.Animation.__init__(self, 0.0, 1.0) self._palette = palette @@ -418,4 +425,3 @@ class _SecondaryAnimation(animator.Animation): def next_frame(self, current): if current == 1.0: self._palette.set_palette_state(Palette.SECONDARY) - diff --git a/src/sugar/graphics/palettegroup.py b/src/sugar/graphics/palettegroup.py index d3bccb5f..5f15bdec 100644 --- a/src/sugar/graphics/palettegroup.py +++ b/src/sugar/graphics/palettegroup.py @@ -21,8 +21,10 @@ STABLE. import gobject + _groups = {} + def get_group(group_id): if _groups.has_key(group_id): group = _groups[group_id] @@ -32,13 +34,14 @@ def get_group(group_id): return group + class Group(gobject.GObject): + __gsignals__ = { - 'popup' : (gobject.SIGNAL_RUN_FIRST, - gobject.TYPE_NONE, ([])), - 'popdown' : (gobject.SIGNAL_RUN_FIRST, - gobject.TYPE_NONE, ([])) + 'popup': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])), + 'popdown': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])), } + def __init__(self): gobject.GObject.__init__(self) self._up = False diff --git a/src/sugar/graphics/palettewindow.py b/src/sugar/graphics/palettewindow.py index d3fe2cbc..b3392aff 100644 --- a/src/sugar/graphics/palettewindow.py +++ b/src/sugar/graphics/palettewindow.py @@ -31,8 +31,9 @@ from sugar.graphics import palettegroup from sugar.graphics import animator from sugar.graphics import style -# Helper function to find the gap position and size of widget a + 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 # adjacent to each other. gap = True @@ -63,6 +64,7 @@ def _calculate_gap(a, b): else: return False + class MouseSpeedDetector(gobject.GObject): __gsignals__ = { @@ -125,17 +127,15 @@ class MouseSpeedDetector(gobject.GObject): return True + class PaletteWindow(gtk.Window): __gtype_name__ = 'SugarPaletteWindow' __gsignals__ = { - 'popup' : (gobject.SIGNAL_RUN_FIRST, - gobject.TYPE_NONE, ([])), - 'popdown' : (gobject.SIGNAL_RUN_FIRST, - gobject.TYPE_NONE, ([])), - 'activate' : (gobject.SIGNAL_RUN_FIRST, - gobject.TYPE_NONE, ([])) + 'popup': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])), + 'popdown': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])), + 'activate': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])), } def __init__(self, **kwargs): @@ -401,7 +401,9 @@ class PaletteWindow(gtk.Window): palette_state = property(get_palette_state) + class _PopupAnimation(animator.Animation): + def __init__(self, palette): animator.Animation.__init__(self, 0.0, 1.0) self._palette = palette @@ -410,7 +412,9 @@ class _PopupAnimation(animator.Animation): if current == 1.0: self._palette.show() + class _PopdownAnimation(animator.Animation): + def __init__(self, palette): animator.Animation.__init__(self, 0.0, 1.0) self._palette = palette @@ -419,27 +423,25 @@ class _PopdownAnimation(animator.Animation): if current == 1.0: self._palette.hide() + class Invoker(gobject.GObject): + __gtype_name__ = 'SugarPaletteInvoker' __gsignals__ = { 'mouse-enter': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])), 'mouse-leave': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])), 'right-click': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])), - 'focus-out': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])) + 'focus-out': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])), } ANCHORED = 0 AT_CURSOR = 1 - BOTTOM = [(0.0, 0.0, 0.0, 1.0), - (-1.0, 0.0, 1.0, 1.0)] - RIGHT = [(0.0, 0.0, 1.0, 0.0), - (0.0, -1.0, 1.0, 1.0)] - TOP = [(0.0, -1.0, 0.0, 0.0), - (-1.0, -1.0, 1.0, 0.0)] - LEFT = [(-1.0, 0.0, 0.0, 0.0), - (-1.0, -1.0, 0.0, 1.0)] + BOTTOM = [(0.0, 0.0, 0.0, 1.0), (-1.0, 0.0, 1.0, 1.0)] + RIGHT = [(0.0, 0.0, 1.0, 0.0), (0.0, -1.0, 1.0, 1.0)] + TOP = [(0.0, -1.0, 0.0, 0.0), (-1.0, -1.0, 1.0, 0.0)] + LEFT = [(-1.0, 0.0, 0.0, 0.0), (-1.0, -1.0, 0.0, 1.0)] def __init__(self): gobject.GObject.__init__(self) @@ -531,8 +533,9 @@ class Invoker(gobject.GObject): alignment = self.get_alignment(palette_dim) rect = self._get_position_for_alignment(alignment, palette_dim) - # In case our efforts to find an optimum place inside the screen failed, - # just make sure the palette fits inside the screen if at all possible. + # In case our efforts to find an optimum place inside the screen + # failed, just make sure the palette fits inside the screen if at all + # possible. rect.x = max(0, rect.x) rect.y = max(0, rect.y) @@ -642,7 +645,9 @@ class Invoker(gobject.GObject): palette = gobject.property( type=object, setter=set_palette, getter=get_palette) + class WidgetInvoker(Invoker): + def __init__(self, parent=None, widget=None): Invoker.__init__(self) @@ -663,11 +668,11 @@ class WidgetInvoker(Invoker): self.notify('widget') self._enter_hid = self._widget.connect('enter-notify-event', - self.__enter_notify_event_cb) + self.__enter_notify_event_cb) self._leave_hid = self._widget.connect('leave-notify-event', - self.__leave_notify_event_cb) + self.__leave_notify_event_cb) self._release_hid = self._widget.connect('button-release-event', - self.__button_release_event_cb) + self.__button_release_event_cb) self.attach(parent) @@ -750,7 +755,9 @@ class WidgetInvoker(Invoker): return self._widget widget = gobject.property(type=object, getter=_get_widget, setter=None) + class CanvasInvoker(Invoker): + def __init__(self, parent=None): Invoker.__init__(self) @@ -806,7 +813,9 @@ class CanvasInvoker(Invoker): def get_toplevel(self): return hippo.get_canvas_for_item(self._item).get_toplevel() + class ToolInvoker(WidgetInvoker): + def __init__(self, parent=None): WidgetInvoker.__init__(self) @@ -826,7 +835,9 @@ class ToolInvoker(WidgetInvoker): else: return self.LEFT + self.RIGHT + class CellRendererInvoker(Invoker): + def __init__(self): Invoker.__init__(self) @@ -910,14 +921,16 @@ class CellRendererInvoker(Invoker): self.notify_mouse_leave() def __button_release_event_cb(self, widget, event): - if event.button == 1 and self._point_in_cell_renderer(event.x, event.y): + if event.button == 1 and self._point_in_cell_renderer(event.x, + event.y): tree_view = self._tree_view path, column_, x_, y_ = tree_view.get_path_at_pos(int(event.x), int(event.y)) self._cell_renderer.emit('clicked', path) # So the treeview receives it and knows a drag isn't going on return False - if event.button == 3 and self._point_in_cell_renderer(event.x, event.y): + if event.button == 3 and self._point_in_cell_renderer(event.x, + event.y): self.notify_right_click() return True else: @@ -951,4 +964,3 @@ class CellRendererInvoker(Invoker): def get_default_position(self): return self.AT_CURSOR - diff --git a/src/sugar/graphics/panel.py b/src/sugar/graphics/panel.py index bc48db82..441e7a94 100644 --- a/src/sugar/graphics/panel.py +++ b/src/sugar/graphics/panel.py @@ -21,7 +21,10 @@ STABLE. import gtk + class Panel(gtk.VBox): + __gtype_name__ = 'SugarPanel' + def __init__(self): gtk.VBox.__init__(self) diff --git a/src/sugar/graphics/radiopalette.py b/src/sugar/graphics/radiopalette.py index ff49e218..81991655 100644 --- a/src/sugar/graphics/radiopalette.py +++ b/src/sugar/graphics/radiopalette.py @@ -20,7 +20,9 @@ import gtk from sugar.graphics.toolbutton import ToolButton from sugar.graphics.palette import Palette + class RadioMenuButton(ToolButton): + def __init__(self, **kwargs): ToolButton.__init__(self, **kwargs) self.selected_button = None @@ -44,7 +46,9 @@ class RadioMenuButton(ToolButton): else: self.palette.popup(immediate=True, state=Palette.SECONDARY) + class RadioToolsButton(RadioMenuButton): + def __init__(self, **kwargs): RadioMenuButton.__init__(self, **kwargs) @@ -53,7 +57,9 @@ class RadioToolsButton(RadioMenuButton): return self.selected_button.emit('clicked') + class RadioPalette(Palette): + def __init__(self, **kwargs): Palette.__init__(self, **kwargs) diff --git a/src/sugar/graphics/radiotoolbutton.py b/src/sugar/graphics/radiotoolbutton.py index 72642326..37267b4b 100644 --- a/src/sugar/graphics/radiotoolbutton.py +++ b/src/sugar/graphics/radiotoolbutton.py @@ -27,11 +27,13 @@ from sugar.graphics.icon import Icon from sugar.graphics.palette import Palette, ToolInvoker from sugar.graphics import toolbutton + class RadioToolButton(gtk.RadioToolButton): """ An implementation of a "push" button. """ + __gtype_name__ = 'SugarRadioToolButton' def __init__(self, **kwargs): @@ -76,7 +78,8 @@ class RadioToolButton(gtk.RadioToolButton): def get_tooltip(self): return self._tooltip - tooltip = gobject.property(type=str, setter=set_tooltip, getter=get_tooltip) + tooltip = gobject.property(type=str, setter=set_tooltip, + getter=get_tooltip) def set_accelerator(self, accelerator): """ @@ -177,4 +180,3 @@ class RadioToolButton(gtk.RadioToolButton): allocation.width, allocation.height) gtk.RadioToolButton.do_expose_event(self, event) - diff --git a/src/sugar/graphics/roundbox.py b/src/sugar/graphics/roundbox.py index b909c741..75141f04 100644 --- a/src/sugar/graphics/roundbox.py +++ b/src/sugar/graphics/roundbox.py @@ -25,6 +25,7 @@ import hippo from sugar.graphics import style + class CanvasRoundBox(hippo.CanvasBox, hippo.CanvasItem): __gtype_name__ = 'SugarRoundBox' diff --git a/src/sugar/graphics/style.py b/src/sugar/graphics/style.py index e63e5caa..6d5e35ec 100644 --- a/src/sugar/graphics/style.py +++ b/src/sugar/graphics/style.py @@ -28,9 +28,11 @@ import logging import gtk import pango + FOCUS_LINE_WIDTH = 2 _TAB_CURVATURE = 1 + def _compute_zoom_factor(): if os.environ.has_key('SUGAR_SCALING'): try: @@ -41,7 +43,9 @@ def _compute_zoom_factor(): return 1.0 + class Font(object): + def __init__(self, desc): self._desc = desc @@ -51,7 +55,9 @@ class Font(object): def get_pango_desc(self): return pango.FontDescription(self._desc) + class Color(object): + def __init__(self, color, alpha=1.0): self._r, self._g, self._b = self._html_to_rgb(color) self._a = alpha @@ -91,9 +97,11 @@ class Color(object): else: return self.get_html() + def zoom(units): return int(ZOOM_FACTOR * units) + ZOOM_FACTOR = _compute_zoom_factor() DEFAULT_SPACING = zoom(15) diff --git a/src/sugar/graphics/toggletoolbutton.py b/src/sugar/graphics/toggletoolbutton.py index 0aeb59ca..cdaf2f01 100644 --- a/src/sugar/graphics/toggletoolbutton.py +++ b/src/sugar/graphics/toggletoolbutton.py @@ -25,7 +25,9 @@ import gtk from sugar.graphics.icon import Icon from sugar.graphics.palette import Palette, ToolInvoker + class ToggleToolButton(gtk.ToggleToolButton): + __gtype_name__ = "SugarToggleToolButton" def __init__(self, named_icon=None): diff --git a/src/sugar/graphics/toolbarbox.py b/src/sugar/graphics/toolbarbox.py index 266e6be4..8cc52dfe 100644 --- a/src/sugar/graphics/toolbarbox.py +++ b/src/sugar/graphics/toolbarbox.py @@ -24,7 +24,9 @@ from sugar.graphics.palette import PaletteWindow, ToolInvoker from sugar.graphics.toolbutton import ToolButton from sugar.graphics import palettegroup + class ToolbarButton(ToolButton): + def __init__(self, page=None, **kwargs): ToolButton.__init__(self, **kwargs) @@ -132,7 +134,9 @@ class ToolbarButton(ToolButton): gtk.ToolButton.do_expose_event(self, event) _paint_arrow(self, event, gtk.ARROW_UP) + class ToolbarBox(gtk.VBox): + def __init__(self, padding=style.TOOLBOX_HORIZONTAL_PADDING): gtk.VBox.__init__(self) self.expanded_button = None @@ -177,7 +181,9 @@ class ToolbarBox(gtk.VBox): self.toolbar.parent.parent.modify_bg(state, color) self.toolbar.modify_bg(state, color) + class _ToolbarPalette(PaletteWindow): + def __init__(self, **kwargs): PaletteWindow.__init__(self, **kwargs) self.toolbar_box = None @@ -232,7 +238,9 @@ class _ToolbarPalette(PaletteWindow): if self._focus == 0: self.popdown(immediate=True) + class _Box(gtk.EventBox): + def __init__(self): gtk.EventBox.__init__(self) self.connect('expose-event', self.do_expose_event) @@ -251,6 +259,7 @@ class _Box(gtk.EventBox): alloc.width - style.FOCUS_LINE_WIDTH * 2, style.FOCUS_LINE_WIDTH) + def _setup_page(page_widget, color, hpad): vpad = style.FOCUS_LINE_WIDTH page_widget.child.set_padding(vpad, vpad, hpad, hpad) @@ -264,6 +273,7 @@ def _setup_page(page_widget, color, hpad): page_widget.modify_bg(gtk.STATE_NORMAL, color) page_widget.modify_bg(gtk.STATE_PRELIGHT, color) + def _embody_page(box_class, widget): widget.show() alignment = gtk.Alignment(0.0, 0.0, 1.0, 1.0) @@ -275,6 +285,7 @@ def _embody_page(box_class, widget): box.show() return box + def _paint_arrow(widget, event, arrow_type): alloc = widget.allocation x = alloc.x + alloc.width / 2 - style.TOOLBAR_ARROW_SIZE / 2 diff --git a/src/sugar/graphics/toolbox.py b/src/sugar/graphics/toolbox.py index 47a311d0..9f29281c 100644 --- a/src/sugar/graphics/toolbox.py +++ b/src/sugar/graphics/toolbox.py @@ -25,13 +25,14 @@ import hippo from sugar.graphics import style + class Toolbox(gtk.VBox): + __gtype_name__ = 'SugarToolbox' __gsignals__ = { 'current-toolbar-changed': (gobject.SIGNAL_RUN_FIRST, - gobject.TYPE_NONE, - ([int])) + gobject.TYPE_NONE, ([int])), } def __init__(self): @@ -98,4 +99,3 @@ class Toolbox(gtk.VBox): return self._notebook.get_current_page() current_toolbar = property(get_current_toolbar, set_current_toolbar) - diff --git a/src/sugar/graphics/toolbutton.py b/src/sugar/graphics/toolbutton.py index 7def0ff7..f15e4068 100644 --- a/src/sugar/graphics/toolbutton.py +++ b/src/sugar/graphics/toolbutton.py @@ -28,6 +28,7 @@ import gobject from sugar.graphics.icon import Icon from sugar.graphics.palette import Palette, ToolInvoker + def _add_accelerator(tool_button): if not tool_button.props.accelerator or not tool_button.get_toplevel() or \ not tool_button.child: @@ -46,14 +47,18 @@ def _add_accelerator(tool_button): tool_button.child.add_accelerator('clicked', accel_group, keyval, mask, gtk.ACCEL_LOCKED | gtk.ACCEL_VISIBLE) + def _hierarchy_changed_cb(tool_button, previous_toplevel): _add_accelerator(tool_button) + def setup_accelerator(tool_button): _add_accelerator(tool_button) tool_button.connect('hierarchy-changed', _hierarchy_changed_cb) + class ToolButton(gtk.ToolButton): + __gtype_name__ = "SugarToolButton" def __init__(self, icon_name=None, **kwargs): @@ -97,7 +102,8 @@ class ToolButton(gtk.ToolButton): def get_tooltip(self): return self._tooltip - tooltip = gobject.property(type=str, setter=set_tooltip, getter=get_tooltip) + tooltip = gobject.property(type=str, setter=set_tooltip, + getter=get_tooltip) def set_accelerator(self, accelerator): self._accelerator = accelerator diff --git a/src/sugar/graphics/toolcombobox.py b/src/sugar/graphics/toolcombobox.py index 16e14bd6..1b2fdb0a 100644 --- a/src/sugar/graphics/toolcombobox.py +++ b/src/sugar/graphics/toolcombobox.py @@ -25,10 +25,11 @@ import gobject from sugar.graphics.combobox import ComboBox from sugar.graphics import style + class ToolComboBox(gtk.ToolItem): + __gproperties__ = { - 'label-text' : (str, None, None, None, - gobject.PARAM_WRITABLE), + 'label-text': (str, None, None, None, gobject.PARAM_WRITABLE), } def __init__(self, combo=None, **kwargs): diff --git a/src/sugar/graphics/tray.py b/src/sugar/graphics/tray.py index 8b0abe78..172123a8 100644 --- a/src/sugar/graphics/tray.py +++ b/src/sugar/graphics/tray.py @@ -27,17 +27,17 @@ from sugar.graphics.palette import ToolInvoker from sugar.graphics.toolbutton import ToolButton from sugar.graphics.icon import Icon + _PREVIOUS_PAGE = 0 _NEXT_PAGE = 1 + class _TrayViewport(gtk.Viewport): + __gproperties__ = { - 'scrollable' : (bool, None, None, False, - gobject.PARAM_READABLE), - 'can-scroll-prev' : (bool, None, None, False, - gobject.PARAM_READABLE), - 'can-scroll-next' : (bool, None, None, False, - gobject.PARAM_READABLE), + 'scrollable': (bool, None, None, False, gobject.PARAM_READABLE), + 'can-scroll-prev': (bool, None, None, False, gobject.PARAM_READABLE), + 'can-scroll-next': (bool, None, None, False, gobject.PARAM_READABLE), } def __init__(self, orientation): @@ -161,6 +161,7 @@ class _TrayViewport(gtk.Viewport): class _TrayScrollButton(ToolButton): + def __init__(self, icon_name, scroll_direction): ToolButton.__init__(self) self._viewport = None @@ -194,7 +195,6 @@ class _TrayScrollButton(ToolButton): self._viewport_can_scroll_dir_changed_cb) self.set_sensitive(self._viewport.props.can_scroll_next) - def _viewport_scrollable_changed_cb(self, viewport, pspec): self.props.visible = self._viewport.props.scrollable @@ -211,19 +211,21 @@ class _TrayScrollButton(ToolButton): viewport = property(fset=set_viewport) + ALIGN_TO_START = 0 ALIGN_TO_END = 1 + class HTray(gtk.HBox): + __gtype_name__ = 'SugarHTray' __gproperties__ = { - 'align' : (int, None, None, 0, 1, ALIGN_TO_START, - gobject.PARAM_READWRITE | - gobject.PARAM_CONSTRUCT_ONLY), - 'drag-active' : (bool, None, None, False, - gobject.PARAM_READWRITE) + 'align': (int, None, None, 0, 1, ALIGN_TO_START, + gobject.PARAM_READWRITE | gobject.PARAM_CONSTRUCT_ONLY), + 'drag-active': (bool, None, None, False, gobject.PARAM_READWRITE), } + def __init__(self, **kwargs): self._drag_active = False self.align = ALIGN_TO_START @@ -299,15 +301,15 @@ class HTray(gtk.HBox): def scroll_to_item(self, item): self._viewport.scroll_to_item(item) + class VTray(gtk.VBox): + __gtype_name__ = 'SugarVTray' __gproperties__ = { - 'align' : (int, None, None, 0, 1, ALIGN_TO_START, - gobject.PARAM_READWRITE | - gobject.PARAM_CONSTRUCT_ONLY), - 'drag-active' : (bool, None, None, False, - gobject.PARAM_READWRITE) + 'align': (int, None, None, 0, 1, ALIGN_TO_START, + gobject.PARAM_READWRITE | gobject.PARAM_CONSTRUCT_ONLY), + 'drag-active': (bool, None, None, False, gobject.PARAM_READWRITE), } def __init__(self, **kwargs): @@ -385,11 +387,15 @@ class VTray(gtk.VBox): def scroll_to_item(self, item): self._viewport.scroll_to_item(item) + class TrayButton(ToolButton): + def __init__(self, **kwargs): ToolButton.__init__(self, **kwargs) + class _IconWidget(gtk.EventBox): + __gtype_name__ = "SugarTrayIconWidget" def __init__(self, icon_name=None, xo_color=None): @@ -413,7 +419,9 @@ class _IconWidget(gtk.EventBox): def get_icon(self): return self._icon + class TrayIcon(gtk.ToolItem): + __gtype_name__ = "SugarTrayIcon" def __init__(self, icon_name=None, xo_color=None): @@ -458,4 +466,3 @@ class TrayIcon(gtk.ToolItem): def get_icon(self): return self._icon_widget.get_icon() icon = property(get_icon, None) - diff --git a/src/sugar/graphics/window.py b/src/sugar/graphics/window.py index 6ab6ee16..9cba30e4 100644 --- a/src/sugar/graphics/window.py +++ b/src/sugar/graphics/window.py @@ -26,8 +26,10 @@ import warnings from sugar.graphics.icon import Icon + _UNFULLSCREEN_BUTTON_VISIBILITY_TIMEOUT = 2 + class UnfullscreenButton(gtk.Window): def __init__(self): @@ -77,7 +79,9 @@ class UnfullscreenButton(gtk.Window): def _screen_size_changed_cb(self, screen): self._reposition() + class Window(gtk.Window): + def __init__(self, **args): self._enable_fullscreen_mode = True @@ -263,8 +267,7 @@ class Window(gtk.Window): return self._enable_fullscreen_mode enable_fullscreen_mode = gobject.property(type=object, - setter=set_enable_fullscreen_mode, - getter=get_enable_fullscreen_mode) + setter=set_enable_fullscreen_mode, getter=get_enable_fullscreen_mode) # DEPRECATED diff --git a/src/sugar/graphics/xocolor.py b/src/sugar/graphics/xocolor.py index beb95651..92a61a44 100644 --- a/src/sugar/graphics/xocolor.py +++ b/src/sugar/graphics/xocolor.py @@ -204,6 +204,7 @@ colors = [ ['#BCCDFF', '#AC32FF'], \ ] + def _parse_string(color_string): if color_string == 'white': return ['#ffffff', '#414141'] @@ -216,10 +217,13 @@ def _parse_string(color_string): else: return None + def is_valid(color_string): return (_parse_string(color_string) != None) + class XoColor: + def __init__(self, color_string=None): if color_string == None or not is_valid(color_string): n = int(random.random() * (len(colors) - 1)) @@ -242,6 +246,7 @@ class XoColor: def to_string(self): return '%s,%s' % (self.stroke, self.fill) + if __name__ == "__main__": import sys import re diff --git a/src/sugar/network.py b/src/sugar/network.py index d6599ff9..bde8c9fc 100644 --- a/src/sugar/network.py +++ b/src/sugar/network.py @@ -29,14 +29,18 @@ import gobject import SimpleHTTPServer import SocketServer + __authinfos = {} + def _add_authinfo(authinfo): __authinfos[threading.currentThread()] = authinfo + def get_authinfo(): return __authinfos.get(threading.currentThread()) + def _del_authinfo(): del __authinfos[threading.currentThread()] @@ -172,16 +176,17 @@ class ChunkedGlibHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): self.end_headers() return f + class GlibURLDownloader(gobject.GObject): """Grabs a URL in chunks, returning to the mainloop after each chunk""" __gsignals__ = { 'finished': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT])), - 'error': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_PYOBJECT])), + ([gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT])), + 'error': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([gobject.TYPE_PYOBJECT])), 'progress': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_PYOBJECT])) + ([gobject.TYPE_PYOBJECT])), } CHUNK_SIZE = 4096 diff --git a/src/sugar/presence/activity.py b/src/sugar/presence/activity.py index 3102388d..1d4a9c9e 100644 --- a/src/sugar/presence/activity.py +++ b/src/sugar/presence/activity.py @@ -26,8 +26,10 @@ import dbus import gobject import telepathy + _logger = logging.getLogger('sugar.presence.activity') + class Activity(gobject.GObject): """UI interface for an Activity in the presence service @@ -43,23 +45,23 @@ class Activity(gobject.GObject): """ __gsignals__ = { 'buddy-joined': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_PYOBJECT])), - 'buddy-left': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_PYOBJECT])), - 'new-channel': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_PYOBJECT])), - 'joined': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT])), + ([gobject.TYPE_PYOBJECT])), + 'buddy-left': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([gobject.TYPE_PYOBJECT])), + 'new-channel': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([gobject.TYPE_PYOBJECT])), + 'joined': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT])), } __gproperties__ = { - 'id' : (str, None, None, None, gobject.PARAM_READABLE), - 'name' : (str, None, None, None, gobject.PARAM_READWRITE), - 'tags' : (str, None, None, None, gobject.PARAM_READWRITE), - 'color' : (str, None, None, None, gobject.PARAM_READWRITE), - 'type' : (str, None, None, None, gobject.PARAM_READABLE), - 'private' : (bool, None, None, True, gobject.PARAM_READWRITE), - 'joined' : (bool, None, None, False, gobject.PARAM_READABLE), + 'id': (str, None, None, None, gobject.PARAM_READABLE), + 'name': (str, None, None, None, gobject.PARAM_READWRITE), + 'tags': (str, None, None, None, gobject.PARAM_READWRITE), + 'color': (str, None, None, None, gobject.PARAM_READWRITE), + 'type': (str, None, None, None, gobject.PARAM_READABLE), + 'private': (bool, None, None, True, gobject.PARAM_READWRITE), + 'joined': (bool, None, None, False, gobject.PARAM_READABLE), } _PRESENCE_SERVICE = "org.laptop.Sugar.Presence" @@ -175,10 +177,10 @@ class Activity(gobject.GObject): elif pspec.name == "private": return self._private - # FIXME: need an asynchronous API to set these properties, particularly - # 'private' def do_set_property(self, pspec, val): """Set a particular property in our property dictionary""" + # FIXME: need an asynchronous API to set these properties, + # particularly 'private' if pspec.name == "name": self._activity.SetProperties({'name': val}) self._name = val diff --git a/src/sugar/presence/buddy.py b/src/sugar/presence/buddy.py index f97682ad..2978e4db 100644 --- a/src/sugar/presence/buddy.py +++ b/src/sugar/presence/buddy.py @@ -24,6 +24,7 @@ import gobject import gtk import dbus + class Buddy(gobject.GObject): """UI interface for a Buddy in the presence service @@ -40,26 +41,26 @@ class Buddy(gobject.GObject): 'icon': (XXX pixel data for an icon?) See __gproperties__ """ + __gsignals__ = { - 'icon-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([])), + 'icon-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])), 'joined-activity': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_PYOBJECT])), + ([gobject.TYPE_PYOBJECT])), 'left-activity': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_PYOBJECT])), + ([gobject.TYPE_PYOBJECT])), 'property-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_PYOBJECT])), + ([gobject.TYPE_PYOBJECT])), } __gproperties__ = { - 'key' : (str, None, None, None, gobject.PARAM_READABLE), - 'icon' : (str, None, None, None, gobject.PARAM_READABLE), - 'nick' : (str, None, None, None, gobject.PARAM_READABLE), - 'color' : (str, None, None, None, gobject.PARAM_READABLE), - 'current-activity' : (object, None, None, gobject.PARAM_READABLE), - 'owner' : (bool, None, None, False, gobject.PARAM_READABLE), - 'ip4-address' : (str, None, None, None, gobject.PARAM_READABLE), - 'tags' : (str, None, None, None, gobject.PARAM_READABLE), + 'key': (str, None, None, None, gobject.PARAM_READABLE), + 'icon': (str, None, None, None, gobject.PARAM_READABLE), + 'nick': (str, None, None, None, gobject.PARAM_READABLE), + 'color': (str, None, None, None, gobject.PARAM_READABLE), + 'current-activity': (object, None, None, gobject.PARAM_READABLE), + 'owner': (bool, None, None, False, gobject.PARAM_READABLE), + 'ip4-address': (str, None, None, None, gobject.PARAM_READABLE), + 'tags': (str, None, None, None, gobject.PARAM_READABLE), } _PRESENCE_SERVICE = "org.laptop.Sugar.Presence" diff --git a/src/sugar/presence/presenceservice.py b/src/sugar/presence/presenceservice.py index f6bb8758..f4aa6df7 100644 --- a/src/sugar/presence/presenceservice.py +++ b/src/sugar/presence/presenceservice.py @@ -63,13 +63,12 @@ class PresenceService(gobject.GObject): ([gobject.TYPE_PYOBJECT])), 'activity-shared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT, - gobject.TYPE_PYOBJECT])) + gobject.TYPE_PYOBJECT])), } _PS_BUDDY_OP = DBUS_PATH + "/Buddies/" _PS_ACTIVITY_OP = DBUS_PATH + "/Activities/" - def __init__(self, allow_offline_iface=True): """Initialise the service and attempt to connect to events """ @@ -98,6 +97,7 @@ class PresenceService(gobject.GObject): self._get_ps() _ps_ = None + def _get_ps(self): """Retrieve dbus interface to PresenceService @@ -123,17 +123,16 @@ class PresenceService(gobject.GObject): self._bus.get_object(DBUS_SERVICE, DBUS_PATH, follow_name_owner_changes=True), - DBUS_INTERFACE - ) + DBUS_INTERFACE) except dbus.exceptions.DBusException, err: _logger.error( """Failure retrieving %r interface from the D-BUS service %r %r: %s""", - DBUS_INTERFACE, DBUS_SERVICE, DBUS_PATH, err - ) + DBUS_INTERFACE, DBUS_SERVICE, DBUS_PATH, err) if self._allow_offline_iface: return _OfflineInterface() - raise RuntimeError("Failed to connect to the presence service.") + raise RuntimeError('Failed to connect to the presence ' + 'service.') else: self._ps_ = ps ps.connect_to_signal('BuddyAppeared', @@ -150,11 +149,9 @@ class PresenceService(gobject.GObject): self._private_invitation_cb) return self._ps_ - _ps = property( - _get_ps, None, None, + _ps = property(_get_ps, None, None, """DBUS interface to the PresenceService - (services/presence/presenceservice)""" - ) + (services/presence/presenceservice)""") def _new_object(self, object_path): """Turn new object path into (cached) Buddy/Activity instance @@ -207,7 +204,8 @@ class PresenceService(gobject.GObject): return False def _buddy_appeared_cb(self, op): - """Callback for dbus event (forwards to method to emit GObject event)""" + """Callback for dbus event (forwards to method to emit GObject + event)""" gobject.idle_add(self._emit_buddy_appeared_signal, op) def _emit_buddy_disappeared_signal(self, object_path): @@ -218,9 +216,9 @@ class PresenceService(gobject.GObject): obj = self._objcache[object_path] self.emit('buddy-disappeared', obj) - # We cannot maintain the object in the cache because that would keep - # a lot of objects from being collected. That includes UI objects - # due to signals using strong references. + # We cannot maintain the object in the cache because that would + # keep a lot of objects from being collected. That includes UI + # objects due to signals using strong references. # If we want to cache some despite the memory usage increase, # we could use a LRU cache limited to some value. del self._objcache[object_path] @@ -229,7 +227,8 @@ class PresenceService(gobject.GObject): return False def _buddy_disappeared_cb(self, object_path): - """Callback for dbus event (forwards to method to emit GObject event)""" + """Callback for dbus event (forwards to method to emit GObject + event)""" gobject.idle_add(self._emit_buddy_disappeared_signal, object_path) def _emit_activity_invitation_signal(self, activity_path, buddy_path, @@ -240,7 +239,8 @@ class PresenceService(gobject.GObject): return False def _activity_invitation_cb(self, activity_path, buddy_path, message): - """Callback for dbus event (forwards to method to emit GObject event)""" + """Callback for dbus event (forwards to method to emit GObject + event)""" gobject.idle_add(self._emit_activity_invitation_signal, activity_path, buddy_path, message) @@ -252,7 +252,8 @@ class PresenceService(gobject.GObject): return False def _private_invitation_cb(self, bus_name, connection, channel, chan_type): - """Callback for dbus event (forwards to method to emit GObject event)""" + """Callback for dbus event (forwards to method to emit GObject + event)""" gobject.idle_add(self._emit_private_invitation_signal, bus_name, connection, channel, chan_type) @@ -262,7 +263,8 @@ class PresenceService(gobject.GObject): return False def _activity_appeared_cb(self, object_path): - """Callback for dbus event (forwards to method to emit GObject event)""" + """Callback for dbus event (forwards to method to emit GObject + event)""" gobject.idle_add(self._emit_activity_appeared_signal, object_path) def _emit_activity_disappeared_signal(self, object_path): @@ -271,7 +273,8 @@ class PresenceService(gobject.GObject): return False def _activity_disappeared_cb(self, object_path): - """Callback for dbus event (forwards to method to emit GObject event)""" + """Callback for dbus event (forwards to method to emit GObject + event)""" gobject.idle_add(self._emit_activity_disappeared_signal, object_path) def get(self, object_path): @@ -288,11 +291,9 @@ class PresenceService(gobject.GObject): """ try: resp = self._ps.GetActivities() - except dbus.exceptions.DBusException, err: - _logger.warn( - """Unable to retrieve activity list from presence service: %s""" - % err - ) + except dbus.exceptions.DBusException: + _logger.exception('Unable to retrieve activity list from ' + 'presence service') return [] else: acts = [] @@ -311,10 +312,8 @@ class PresenceService(gobject.GObject): if error_handler: error_handler(e) else: - _logger.warn( - """Unable to retrieve activity-list from presence service: %s""" - % e - ) + _logger.warn('Unable to retrieve activity-list from presence ' + 'service: %s', e) def get_activities_async(self, reply_handler=None, error_handler=None): """Retrieve set of all activities from service asyncronously @@ -331,7 +330,6 @@ class PresenceService(gobject.GObject): error_handler=lambda e: \ self._get_activities_error_cb(error_handler, e)) - def get_activity(self, activity_id, warn_if_none=True): """Retrieve single Activity object for the given unique id @@ -357,11 +355,9 @@ class PresenceService(gobject.GObject): """ try: resp = self._ps.GetBuddies() - except dbus.exceptions.DBusException, err: - _logger.warn( - """Unable to retrieve buddy-list from presence service: %s""" - % err - ) + except dbus.exceptions.DBusException: + _logger.exception('Unable to retrieve buddy-list from presence ' + 'service') return [] else: buddies = [] @@ -380,10 +376,8 @@ class PresenceService(gobject.GObject): if error_handler: error_handler(e) else: - _logger.warn( - """Unable to retrieve buddy-list from presence service: %s""" - % e - ) + _logger.warn('Unable to retrieve buddy-list from presence ' + 'service: %s', e) def get_buddies_async(self, reply_handler=None, error_handler=None): """Retrieve set of all buddies from service asyncronously @@ -411,12 +405,9 @@ class PresenceService(gobject.GObject): """ try: buddy_op = self._ps.GetBuddyByPublicKey(dbus.ByteArray(key)) - except dbus.exceptions.DBusException, err: - _logger.warn( - """Unable to retrieve buddy handle - for %r from presence service: %s""" - % key, err - ) + except dbus.exceptions.DBusException: + _logger.exception('Unable to retrieve buddy handle for %r from ' + 'presence service', key) return None return self._new_object(buddy_op) @@ -450,12 +441,9 @@ class PresenceService(gobject.GObject): """Retrieves the laptop "owner" Buddy object.""" try: owner_op = self._ps.GetOwner() - except dbus.exceptions.DBusException, err: - _logger.warn( - """Unable to retrieve local user/owner - from presence service: %s""" - % err - ) + except dbus.exceptions.DBusException: + _logger.exception('Unable to retrieve local user/owner from ' + 'presence service') raise RuntimeError("Could not get owner object.") return self._new_object(owner_op) @@ -526,7 +514,8 @@ class PresenceService(gobject.GObject): return bus_name, object_path -class _OfflineInterface( object ): + +class _OfflineInterface(object): """Offline-presence-service interface Used to mimic the behaviour of a real PresenceService sufficiently @@ -535,33 +524,33 @@ class _OfflineInterface( object ): XXX we could likely return a "MockOwner" object reasonably easily, but would it be worth it? """ - def raiseException( self, *args, **named ): + + def raiseException(self, *args, **named): """Raise dbus.exceptions.DBusException""" - raise dbus.exceptions.DBusException( - """PresenceService Interface not available""" - ) + raise dbus.exceptions.DBusException('PresenceService Interface not ' + 'available') + GetActivities = raiseException GetActivityById = raiseException GetBuddies = raiseException GetBuddyByPublicKey = raiseException GetOwner = raiseException GetPreferredConnection = raiseException - def ShareActivity( - self, actid, atype, name, properties, - reply_handler, error_handler, - ): + + def ShareActivity(self, actid, atype, name, properties, reply_handler, + error_handler): """Pretend to share and fail...""" - exc = IOError( - """Unable to share activity as PresenceService - is not currenly available""" - ) - return error_handler( exc ) + exc = IOError('Unable to share activity as PresenceService is not ' + 'currently available') + return error_handler(exc) + class _MockPresenceService(gobject.GObject): """Test fixture allowing testing of items that use PresenceService See PresenceService for usage and purpose """ + __gsignals__ = { 'buddy-appeared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])), @@ -575,7 +564,7 @@ class _MockPresenceService(gobject.GObject): 'activity-appeared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])), 'activity-disappeared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_PYOBJECT])) + ([gobject.TYPE_PYOBJECT])), } def __init__(self): @@ -599,11 +588,13 @@ class _MockPresenceService(gobject.GObject): def share_activity(self, activity, properties=None): return None + _ps = None + + def get_instance(allow_offline_iface=False): """Retrieve this process' view of the PresenceService""" global _ps if not _ps: _ps = PresenceService(allow_offline_iface) return _ps - diff --git a/src/sugar/presence/tubeconn.py b/src/sugar/presence/tubeconn.py index 8606db66..88cc7292 100644 --- a/src/sugar/presence/tubeconn.py +++ b/src/sugar/presence/tubeconn.py @@ -20,7 +20,7 @@ STABLE. """ -__all__ = ('TubeConnection',) +__all__ = ('TubeConnection', ) __docformat__ = 'reStructuredText' @@ -34,10 +34,10 @@ logger = logging.getLogger('telepathy.tubeconn') class TubeConnection(Connection): - # pylint: disable-msg=W0212 - # Confused by __new__ def __new__(cls, conn, tubes_iface, tube_id, address=None, group_iface=None, mainloop=None): + # pylint: disable-msg=W0212 + # Confused by __new__ if address is None: address = tubes_iface.GetDBusTubeAddress(tube_id) self = super(TubeConnection, cls).__new__(cls, address, @@ -58,9 +58,9 @@ class TubeConnection(Connection): return self - # pylint: disable-msg=W0201 - # Confused by __new__ def _on_get_self_handle_reply(self, handle): + # pylint: disable-msg=W0201 + # Confused by __new__ self.self_handle = handle match = self._tubes_iface.connect_to_signal('DBusNamesChanged', self._on_dbus_names_changed) diff --git a/src/sugar/profile.py b/src/sugar/profile.py index 666c3552..e5f5dc7e 100644 --- a/src/sugar/profile.py +++ b/src/sugar/profile.py @@ -29,8 +29,10 @@ from sugar import env from sugar import util from sugar.graphics.xocolor import XoColor + _profile = None + class Profile(object): """Local user's current options/profile information @@ -46,6 +48,7 @@ class Profile(object): pubkey -- public ssh key privkey_hash -- SHA has of the child's public key """ + def __init__(self, path): self._pubkey = None self._privkey_hash = None @@ -194,6 +197,7 @@ class Profile(object): fd.write(text) fd.close() + def get_profile(): global _profile @@ -203,14 +207,17 @@ def get_profile(): return _profile + def get_nick_name(): client = gconf.client_get_default() return client.get_string("/desktop/sugar/user/nick") + def get_color(): client = gconf.client_get_default() color = client.get_string("/desktop/sugar/user/color") return XoColor(color) + def get_pubkey(): return get_profile().pubkey diff --git a/src/sugar/session.py b/src/sugar/session.py index 0978be86..4ebc5902 100644 --- a/src/sugar/session.py +++ b/src/sugar/session.py @@ -23,11 +23,15 @@ import os from sugar import _sugarext + class XSMPClient(_sugarext.SMClientXSMP): + def __init__(self): _sugarext.SMClientXSMP.__init__(self) + class SessionManager(object): + def __init__(self): address = _sugarext.xsmp_init() os.environ['SESSION_MANAGER'] = address diff --git a/src/sugar/util.py b/src/sugar/util.py index 99092ee5..7b331411 100644 --- a/src/sugar/util.py +++ b/src/sugar/util.py @@ -31,8 +31,10 @@ import logging import atexit import traceback + _ = lambda msg: gettext.dgettext('sugar-toolkit', msg) + def printable_hash(in_hash): """Convert binary hash data into printable characters.""" printable = "" @@ -40,12 +42,14 @@ def printable_hash(in_hash): printable = printable + binascii.b2a_hex(char) return printable + def sha_data(data): """sha1 hash some bytes.""" sha_hash = hashlib.sha1() sha_hash.update(data) return sha_hash.digest() + def unique_id(data = ''): """Generate a likely-unique ID for whatever purpose @@ -66,6 +70,7 @@ def unique_id(data = ''): ACTIVITY_ID_LEN = 40 + def is_hex(s): try: int(s, 16) @@ -74,6 +79,7 @@ def is_hex(s): return True + def validate_activity_id(actid): """Validate an activity ID.""" if not isinstance(actid, (str, unicode)): @@ -84,6 +90,7 @@ def validate_activity_id(actid): return False return True + def set_proc_title(title): """Sets the process title so ps and top show more descriptive names. This does not modify argv[0] @@ -106,13 +113,17 @@ def set_proc_title(title): except Exception: return False + class Node(object): + __slots__ = ['prev', 'next', 'me'] + def __init__(self, prev, me): self.prev = prev self.me = me self.next = None + class LRU: """ Implementation of a length-limited O(1) LRU queue. @@ -120,20 +131,24 @@ class LRU: http://pype.sourceforge.net Copyright 2003 Josiah Carlson. """ - # pylint: disable-msg=W0102,W0612 + def __init__(self, count, pairs=[]): + # pylint: disable-msg=W0102,W0612 self.count = max(count, 1) self.d = {} self.first = None self.last = None for key, value in pairs: self[key] = value + def __contains__(self, obj): return obj in self.d + def __getitem__(self, obj): a = self.d[obj].me self[a[0]] = a[1] return a[1] + def __setitem__(self, obj, val): if obj in self.d: del self[obj] @@ -155,6 +170,7 @@ class LRU: a.next = None del self.d[a.me[0]] del a + def __delitem__(self, obj): nobj = self.d[obj] if nobj.prev: @@ -166,31 +182,37 @@ class LRU: else: self.last = nobj.prev del self.d[obj] + def __iter__(self): cur = self.first while cur != None: cur2 = cur.next yield cur.me[1] cur = cur2 + def iteritems(self): cur = self.first while cur != None: cur2 = cur.next yield cur.me cur = cur2 + def iterkeys(self): return iter(self.d) + def itervalues(self): for i, j in self.iteritems(): yield j + def keys(self): return self.d.keys() -units = [['%d year', '%d years', 356 * 24 * 60 * 60], - ['%d month', '%d months', 30 * 24 * 60 * 60], - ['%d week', '%d weeks', 7 * 24 * 60 * 60], - ['%d day', '%d days', 24 * 60 * 60], - ['%d hour', '%d hours', 60 * 60], + +units = [['%d year', '%d years', 356 * 24 * 60 * 60], + ['%d month', '%d months', 30 * 24 * 60 * 60], + ['%d week', '%d weeks', 7 * 24 * 60 * 60], + ['%d day', '%d days', 24 * 60 * 60], + ['%d hour', '%d hours', 60 * 60], ['%d minute', '%d minutes', 60]] AND = _(' and ') @@ -210,24 +232,28 @@ ELAPSED = _('%s ago') # strings need to be used, then we need to call ngettext() in a fake way so # xgettext will pick them up as plurals. + def ngettext(singular, plural, n): pass + # TRANS: Relative dates (eg. 1 month and 5 days). -ngettext('%d year', '%d years', 1) -ngettext('%d month', '%d months', 1) -ngettext('%d week', '%d weeks', 1) -ngettext('%d day', '%d days', 1) -ngettext('%d hour', '%d hours', 1) +ngettext('%d year', '%d years', 1) +ngettext('%d month', '%d months', 1) +ngettext('%d week', '%d weeks', 1) +ngettext('%d day', '%d days', 1) +ngettext('%d hour', '%d hours', 1) ngettext('%d minute', '%d minutes', 1) del ngettext # End of plurals hack + # gettext perfs hack (#7959) _i18n_timestamps_cache = LRU(60) + def timestamp_to_elapsed_string(timestamp, max_levels=2): levels = 0 time_period = '' @@ -265,9 +291,12 @@ def timestamp_to_elapsed_string(timestamp, max_levels=2): return ELAPSED % time_period + _tracked_paths = {} + class TempFilePath(str): + def __new__(cls, path=None): if path is None: fd, path = tempfile.mkstemp() @@ -293,6 +322,7 @@ class TempFilePath(str): else: _tracked_paths[self] -= 1 + def _cleanup_temp_files(): logging.debug('_cleanup_temp_files') for path in _tracked_paths.keys(): diff --git a/src/sugar/wm.py b/src/sugar/wm.py index 64505e8c..d813d94c 100644 --- a/src/sugar/wm.py +++ b/src/sugar/wm.py @@ -21,6 +21,7 @@ UNSTABLE. Used only internally by Activity and jarabe. import gtk + def get_activity_id(wnck_window): window = gtk.gdk.window_foreign_new(wnck_window.get_xid()) prop_info = window.property_get('_SUGAR_ACTIVITY_ID', 'STRING') @@ -29,6 +30,7 @@ def get_activity_id(wnck_window): else: return prop_info[2] + def get_bundle_id(wnck_window): window = gtk.gdk.window_foreign_new(wnck_window.get_xid()) prop_info = window.property_get('_SUGAR_BUNDLE_ID', 'STRING') @@ -37,10 +39,12 @@ def get_bundle_id(wnck_window): else: return prop_info[2] + def set_activity_id(window, activity_id): window.property_change('_SUGAR_ACTIVITY_ID', 'STRING', 8, gtk.gdk.PROP_MODE_REPLACE, activity_id) + def set_bundle_id(window, bundle_id): window.property_change('_SUGAR_BUNDLE_ID', 'STRING', 8, gtk.gdk.PROP_MODE_REPLACE, bundle_id)