diff --git a/configure.ac b/configure.ac index 5b020024..935c4ebb 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ -AC_INIT([sugar-toolkit],[0.81.5],[],[sugar-toolkit]) +AC_INIT([sugar-toolkit],[0.81.6],[],[sugar-toolkit]) AC_PREREQ([2.59]) diff --git a/src/sugar/activity/activity.py b/src/sugar/activity/activity.py index 3c35de52..95d2960d 100644 --- a/src/sugar/activity/activity.py +++ b/src/sugar/activity/activity.py @@ -439,6 +439,7 @@ class Activity(Window, gtk.Container): self._deleting = False self._max_participants = 0 self._invites_queue = [] + self._jobject = None self._xsmp_client = XSMPClient() self._xsmp_client.connect('quit-requested', self.__sm_quit_requested_cb) @@ -467,33 +468,6 @@ class Activity(Window, gtk.Container): if self._jobject.metadata.has_key('share-scope'): share_scope = self._jobject.metadata['share-scope'] - elif create_jobject: - logging.debug('Creating a jobject.') - self._jobject = datastore.create() - title = _('%s Activity') % get_bundle_name() - self._jobject.metadata['title'] = title - self.set_title(self._jobject.metadata['title']) - self._jobject.metadata['title_set_by_user'] = '0' - self._jobject.metadata['activity'] = self.get_bundle_id() - self._jobject.metadata['activity_id'] = self.get_id() - self._jobject.metadata['keep'] = '0' - self._jobject.metadata['preview'] = '' - self._jobject.metadata['share-scope'] = SCOPE_PRIVATE - - if self._shared_activity is not None: - icon_color = self._shared_activity.props.color - else: - icon_color = profile.get_color().to_string() - - self._jobject.metadata['icon-color'] = icon_color - - self._jobject.file_path = '' - # Cannot call datastore.write async for creates: - # https://dev.laptop.org/ticket/3071 - datastore.write(self._jobject) - else: - self._jobject = None - # handle activity share/join mesh_instance = self._pservice.get_activity(self._activity_id, warn_if_none=False) @@ -524,6 +498,29 @@ class Activity(Window, gtk.Container): else: logging.debug("Unknown share scope %r" % share_scope) + if handle.object_id is None and create_jobject: + logging.debug('Creating a jobject.') + self._jobject = datastore.create() + title = _('%s Activity') % get_bundle_name() + self._jobject.metadata['title'] = title + self.set_title(self._jobject.metadata['title']) + self._jobject.metadata['title_set_by_user'] = '0' + self._jobject.metadata['activity'] = self.get_bundle_id() + self._jobject.metadata['activity_id'] = self.get_id() + self._jobject.metadata['keep'] = '0' + self._jobject.metadata['preview'] = '' + self._jobject.metadata['share-scope'] = SCOPE_PRIVATE + if self._shared_activity is not None: + icon_color = self._shared_activity.props.color + else: + icon_color = profile.get_color().to_string() + self._jobject.metadata['icon-color'] = icon_color + + self._jobject.file_path = '' + # Cannot call datastore.write async for creates: + # https://dev.laptop.org/ticket/3071 + datastore.write(self._jobject) + def do_set_property(self, pspec, value): if pspec.name == 'active': if self._active != value: diff --git a/src/sugar/bundle/activitybundle.py b/src/sugar/bundle/activitybundle.py index 6607e962..b126bd30 100644 --- a/src/sugar/bundle/activitybundle.py +++ b/src/sugar/bundle/activitybundle.py @@ -23,8 +23,7 @@ import os import tempfile from sugar.bundle.bundle import Bundle, MalformedBundleException, \ - AlreadyInstalledException, RegistrationException, \ - NotInstalledException + AlreadyInstalledException, RegistrationException, NotInstalledException from sugar import activity from sugar import env @@ -55,6 +54,7 @@ class ActivityBundle(Bundle): self._mime_types = None self._show_launcher = True self._activity_version = 0 + self._installation_time = os.stat(path).st_mtime info_file = self.get_file('activity/activity.info') if info_file is None: @@ -65,7 +65,7 @@ class ActivityBundle(Bundle): if linfo_file: self._parse_linfo(linfo_file) - self.manifest = None #This should be replaced by following function + self.manifest = None # This should be replaced by following function self.read_manifest() def _raw_manifest(self): @@ -226,7 +226,7 @@ class ActivityBundle(Bundle): def get_installation_time(self): """Get a timestamp representing the time at which this activity was installed.""" - return os.stat(self._path).st_mtime + return self._installation_time def get_bundle_id(self): """Get the activity bundle id""" diff --git a/src/sugar/graphics/icon.py b/src/sugar/graphics/icon.py index 24b5ca53..f99eadee 100644 --- a/src/sugar/graphics/icon.py +++ b/src/sugar/graphics/icon.py @@ -581,11 +581,10 @@ class CanvasIcon(hippo.CanvasBox, hippo.CanvasItem): palette = property(get_palette, set_palette) def get_icon_state(base_name, perc, step=5): - assert step > 0, 'get_icon_state(): step must be positive\n' strength = round(perc / step) * step icon_theme = gtk.icon_theme_get_default() - while strength <= 100: + while strength <= 100 and strength >= 0: icon_name = '%s-%03d' % (base_name, strength) if icon_theme.has_icon(icon_name): return icon_name diff --git a/src/sugar/graphics/tray.py b/src/sugar/graphics/tray.py index 2f5db9aa..8296a531 100644 --- a/src/sugar/graphics/tray.py +++ b/src/sugar/graphics/tray.py @@ -19,7 +19,7 @@ import gobject import gtk from sugar.graphics import style -from sugar.graphics.palette import Palette, ToolInvoker +from sugar.graphics.palette import ToolInvoker from sugar.graphics.toolbutton import ToolButton from sugar.graphics.icon import Icon @@ -207,10 +207,12 @@ class _TrayScrollButton(ToolButton): viewport = property(fset=set_viewport) +ALIGN_TO_START = 0 +ALIGN_TO_END = 1 + class HTray(gtk.HBox): def __init__(self, **kwargs): gobject.GObject.__init__(self, **kwargs) - self.set_direction(gtk.TEXT_DIR_LTR) scroll_left = _TrayScrollButton('go-left', _PREVIOUS_PAGE) self.pack_start(scroll_left, False) @@ -225,17 +227,37 @@ class HTray(gtk.HBox): scroll_left.viewport = self._viewport scroll_right.viewport = self._viewport + if self.align == ALIGN_TO_END: + spacer = gtk.SeparatorToolItem() + spacer.set_size_request(0, 0) + spacer.props.draw = False + spacer.set_expand(True) + self._viewport.traybar.insert(spacer, 0) + spacer.show() + + align = gobject.property( + flags=gobject.PARAM_CONSTRUCT_ONLY | gobject.PARAM_READWRITE, + default=ALIGN_TO_START, type=int) + def get_children(self): - return self._viewport.traybar.get_children() + children = self._viewport.traybar.get_children()[:] + if self.align == ALIGN_TO_END: + children = children[1:] + return children def add_item(self, item, index=-1): + if self.align == ALIGN_TO_END and index > -1: + index += 1 self._viewport.traybar.insert(item, index) def remove_item(self, item): self._viewport.traybar.remove(item) def get_item_index(self, item): - return self._viewport.traybar.get_item_index(item) + index = self._viewport.traybar.get_item_index(item) + if self.align == ALIGN_TO_END: + index -= 1 + return index def scroll_to_item(self, item): self._viewport.scroll_to_item(item) @@ -259,17 +281,37 @@ class VTray(gtk.VBox): scroll_left.viewport = self._viewport scroll_right.viewport = self._viewport + if self.align == ALIGN_TO_END: + spacer = gtk.SeparatorToolItem() + spacer.set_size_request(0, 0) + spacer.props.draw = False + spacer.set_expand(True) + self._viewport.traybar.insert(spacer, 0) + spacer.show() + + align = gobject.property( + flags=gobject.PARAM_CONSTRUCT_ONLY | gobject.PARAM_READWRITE, + default=ALIGN_TO_START, type=int) + def get_children(self): - return self._viewport.traybar.get_children() + children = self._viewport.traybar.get_children()[:] + if self.align == ALIGN_TO_END: + children = children[1:] + return children def add_item(self, item, index=-1): + if self.align == ALIGN_TO_END and index > -1: + index += 1 self._viewport.traybar.insert(item, index) def remove_item(self, item): self._viewport.traybar.remove(item) def get_item_index(self, item): - return self._viewport.traybar.get_item_index(item) + index = self._viewport.traybar.get_item_index(item) + if self.align == ALIGN_TO_END: + index -= 1 + return index def scroll_to_item(self, item): self._viewport.scroll_to_item(item) diff --git a/src/sugar/presence/activity.py b/src/sugar/presence/activity.py index b6351a88..ab3cffff 100644 --- a/src/sugar/presence/activity.py +++ b/src/sugar/presence/activity.py @@ -235,12 +235,18 @@ class Activity(gobject.GObject): def get_joined_buddies(self): """Retrieve the set of Buddy objects attached to this activity - returns list of presence Buddy objects + returns list of presence Buddy objects that we can successfully + create from the buddy object paths that PS has for this activity. """ resp = self._activity.GetJoinedBuddies() buddies = [] for item in resp: - buddies.append(self._ps_new_object(item)) + try: + buddies.append(self._ps_new_object(item)) + except dbus.DBusException: + _logger.debug( + 'get_joined_buddies failed to get buddy object for %r', + item) return buddies def get_buddy_by_handle(self, handle): diff --git a/src/sugar/profile.py b/src/sugar/profile.py index dc9e1d4c..87ce9926 100644 --- a/src/sugar/profile.py +++ b/src/sugar/profile.py @@ -30,6 +30,10 @@ DEFAULT_TIMEZONE = 'UTC' DEFAULT_HOT_CORNERS_DELAY = 0.0 DEFAULT_WARM_EDGES_DELAY = 1000.0 +RING_LAYOUT = 'ring-layout' +RANDOM_LAYOUT = 'random-layout' +DEFAULT_FAVORITES_LAYOUT = RANDOM_LAYOUT + _profile = None def _set_key(cp, section, key, value): @@ -71,6 +75,9 @@ class Profile(object): self.sound_volume = DEFAULT_VOLUME self.hot_corners_delay = DEFAULT_HOT_CORNERS_DELAY self.warm_edges_delay = DEFAULT_WARM_EDGES_DELAY + self.automatic_pm = False + self.extreme_pm = False + self.favorites_layout = DEFAULT_FAVORITES_LAYOUT self._pubkey = None self._privkey_hash = None @@ -110,6 +117,12 @@ class Profile(object): _set_key(cp, 'Sound', 'Volume', self.sound_volume) + _set_key(cp, 'Power', 'AutomaticPM', self.automatic_pm) + + _set_key(cp, 'Power', 'ExtremePM', self.extreme_pm) + + _set_key(cp, 'Shell', 'FavoritesLayout', self.favorites_layout) + f = open(self._config_path, 'w') cp.write(f) f.close() @@ -140,6 +153,16 @@ class Profile(object): self.backup1 = cp.get('Server', 'Backup1') if cp.has_option('Sound', 'Volume'): self.sound_volume = float(cp.get('Sound', 'Volume')) + if cp.has_option('Power', 'AutomaticPM'): + state = cp.get('Power', 'AutomaticPM') + if state.lower() == "true": + self.automatic_pm = True + if cp.has_option('Power', 'ExtremePM'): + state = cp.get('Power', 'ExtremePM') + if state.lower() == "true": + self.extreme_pm = True + if cp.has_option('Shell', 'FavoritesLayout'): + self.favorites_layout = cp.get('Shell', 'FavoritesLayout') del cp