diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/sugar/activity/activityfactory.py | 2 | ||||
-rw-r--r-- | src/sugar/activity/registry.py | 8 | ||||
-rw-r--r-- | src/sugar/bundle/activitybundle.py | 5 | ||||
-rw-r--r-- | src/sugar/graphics/palette.py | 76 | ||||
-rw-r--r-- | src/sugar/util.py | 70 |
5 files changed, 139 insertions, 22 deletions
diff --git a/src/sugar/activity/activityfactory.py b/src/sugar/activity/activityfactory.py index 42b8c40..4d6871f 100644 --- a/src/sugar/activity/activityfactory.py +++ b/src/sugar/activity/activityfactory.py @@ -195,7 +195,7 @@ class ActivityCreationHandler(gobject.GObject): self._use_rainbow = os.path.exists('/etc/olpc-security') if service_name in [ 'org.laptop.JournalActivity', 'org.laptop.Terminal', - 'org.laptop.LogViewer', + 'org.laptop.Log', 'org.laptop.Analyze' ]: self._use_rainbow = False diff --git a/src/sugar/activity/registry.py b/src/sugar/activity/registry.py index 5f5aefc..d5d0529 100644 --- a/src/sugar/activity/registry.py +++ b/src/sugar/activity/registry.py @@ -31,11 +31,12 @@ def _activity_info_from_dict(info_dict): return ActivityInfo(info_dict['name'], info_dict['icon'], info_dict['bundle_id'], info_dict['version'], info_dict['path'], info_dict['show_launcher'], - info_dict['command'], info_dict['favorite']) + info_dict['command'], info_dict['favorite'], + info_dict['installation_time']) class ActivityInfo(object): - def __init__(self, name, icon, bundle_id, version, - path, show_launcher, command, favorite): + def __init__(self, name, icon, bundle_id, version, path, show_launcher, + command, favorite, installation_time): self.name = name self.icon = icon self.bundle_id = bundle_id @@ -44,6 +45,7 @@ class ActivityInfo(object): self.command = command self.show_launcher = show_launcher self.favorite = favorite + self.installation_time = installation_time class ActivityRegistry(gobject.GObject): __gsignals__ = { diff --git a/src/sugar/bundle/activitybundle.py b/src/sugar/bundle/activitybundle.py index 5f1fb7b..db30555 100644 --- a/src/sugar/bundle/activitybundle.py +++ b/src/sugar/bundle/activitybundle.py @@ -163,6 +163,11 @@ class ActivityBundle(Bundle): """Get the activity user visible name.""" return self._name + def get_installation_time(self): + """Get a timestamp representing the time at which this activity was + installed.""" + return os.stat(self._path).st_mtime + def get_bundle_id(self): """Get the activity bundle id""" return self._bundle_id diff --git a/src/sugar/graphics/palette.py b/src/sugar/graphics/palette.py index 592a6df..82feca1 100644 --- a/src/sugar/graphics/palette.py +++ b/src/sugar/graphics/palette.py @@ -209,10 +209,10 @@ class Palette(gtk.Window): self._menu_content_separator = gtk.HSeparator() - self._popup_anim = animator.Animator(0.3, 10) + self._popup_anim = animator.Animator(.5, 10) self._popup_anim.add(_PopupAnimation(self)) - self._secondary_anim = animator.Animator(1.0, 10) + self._secondary_anim = animator.Animator(2.0, 10) self._secondary_anim.add(_SecondaryAnimation(self)) self._popdown_anim = animator.Animator(0.6, 10) @@ -265,10 +265,8 @@ class Palette(gtk.Window): # The menu is not shown here until an item is added self.menu = _Menu(self) - self.connect('enter-notify-event', - self._enter_notify_event_cb) - self.connect('leave-notify-event', - self._leave_notify_event_cb) + self.connect('enter-notify-event', self.__enter_notify_event_cb) + self.connect('leave-notify-event', self.__leave_notify_event_cb) self._mouse_detector = MouseSpeedDetector(self, 200, 5) self._mouse_detector.connect('motion-slow', self._mouse_slow_cb) @@ -314,6 +312,7 @@ class Palette(gtk.Window): if self._invoker is not None: self._invoker.disconnect(self._enter_invoker_hid) self._invoker.disconnect(self._leave_invoker_hid) + self._invoker.disconnect(self._right_click_invoker_hid) self._invoker = invoker if invoker is not None: @@ -321,6 +320,8 @@ class Palette(gtk.Window): 'mouse-enter', self._invoker_mouse_enter_cb) self._leave_invoker_hid = self._invoker.connect( 'mouse-leave', self._invoker_mouse_leave_cb) + self._right_click_invoker_hid = self._invoker.connect( + 'right-click', self._invoker_right_click_cb) if hasattr(invoker.props, 'widget'): self._label.props.accel_widget = invoker.props.widget @@ -586,9 +587,6 @@ class Palette(gtk.Window): self.palette_state = state - def _invoker_mouse_enter_cb(self, invoker): - self._mouse_detector.start() - def _mouse_slow_cb(self, widget): self._mouse_detector.stop() self._palette_do_popup() @@ -610,16 +608,26 @@ class Palette(gtk.Window): self.popup(immediate=immediate) + def _invoker_mouse_enter_cb(self, invoker): + self._mouse_detector.start() + def _invoker_mouse_leave_cb(self, invoker): self._mouse_detector.stop() self.popdown() - def _enter_notify_event_cb(self, widget, event): + def _invoker_right_click_cb(self, invoker): + self._popup_anim.stop() + self._secondary_anim.stop() + self._popdown_anim.stop() + self._set_state(self.SECONDARY) + self._show() + + def __enter_notify_event_cb(self, widget, event): if event.detail != gtk.gdk.NOTIFY_INFERIOR: self._popdown_anim.stop() self._secondary_anim.start() - def _leave_notify_event_cb(self, widget, event): + def __leave_notify_event_cb(self, widget, event): if event.detail != gtk.gdk.NOTIFY_INFERIOR: self.popdown() @@ -706,6 +714,7 @@ class Invoker(gobject.GObject): __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, ([])) } @@ -877,17 +886,23 @@ class Invoker(gobject.GObject): self._cursor_x = -1 self._cursor_y = -1 - def notify_mouse_enter(self): + def _ensure_palette_exists(self): if self.parent and self.palette is None: palette = self.parent.create_palette() if palette: self.palette = palette + def notify_mouse_enter(self): + self._ensure_palette_exists() self.emit('mouse-enter') def notify_mouse_leave(self): self.emit('mouse-leave') + def notify_right_click(self): + self._ensure_palette_exists() + self.emit('right-click') + def get_palette(self): return self._palette @@ -908,6 +923,9 @@ class WidgetInvoker(Invoker): Invoker.__init__(self) self._widget = None + self._enter_hid = None + self._leave_hid = None + self._release_hid = None if parent or widget: self.attach_widget(parent, widget) @@ -919,9 +937,11 @@ class WidgetInvoker(Invoker): self._widget = parent self._enter_hid = widget.connect('enter-notify-event', - self._enter_notify_event_cb) + self.__enter_notify_event_cb) self._leave_hid = widget.connect('leave-notify-event', - self._leave_notify_event_cb) + self.__leave_notify_event_cb) + self._release_hid = widget.connect('button-release-event', + self.__button_release_event_cb) self.attach(parent) @@ -929,6 +949,7 @@ class WidgetInvoker(Invoker): Invoker.detach(self) self._widget.disconnect(self._enter_hid) self._widget.disconnect(self._leave_hid) + self._widget.disconnect(self._release_hid) def get_rect(self): allocation = self._widget.get_allocation() @@ -973,12 +994,19 @@ class WidgetInvoker(Invoker): self._widget.allocation.width, self._widget.allocation.height) - def _enter_notify_event_cb(self, widget, event): + def __enter_notify_event_cb(self, widget, event): self.notify_mouse_enter() - def _leave_notify_event_cb(self, widget, event): + def __leave_notify_event_cb(self, widget, event): self.notify_mouse_leave() + def __button_release_event_cb(self, widget, event): + if event.button == 3: + self.notify_right_click() + return True + else: + return False + def get_toplevel(self): return self._widget.get_toplevel() @@ -999,6 +1027,8 @@ class CanvasInvoker(Invoker): Invoker.__init__(self) self._position_hint = self.AT_CURSOR + self._motion_hid = None + self._release_hid = None if parent: self.attach(parent) @@ -1008,11 +1038,14 @@ class CanvasInvoker(Invoker): self._item = parent self._motion_hid = self._item.connect('motion-notify-event', - self._motion_notify_event_cb) + self.__motion_notify_event_cb) + self._release_hid = self._item.connect('button-release-event', + self.__button_release_event_cb) def detach(self): Invoker.detach(self) self._item.disconnect(self._motion_hid) + self._item.disconnect(self._release_hid) def get_default_position(self): return self.AT_CURSOR @@ -1026,7 +1059,7 @@ class CanvasInvoker(Invoker): else: return gtk.gdk.Rectangle() - def _motion_notify_event_cb(self, button, event): + def __motion_notify_event_cb(self, button, event): if event.detail == hippo.MOTION_DETAIL_ENTER: self.notify_mouse_enter() elif event.detail == hippo.MOTION_DETAIL_LEAVE: @@ -1034,6 +1067,13 @@ class CanvasInvoker(Invoker): return False + def __button_release_event_cb(self, button, event): + if event.button == 3: + self.notify_right_click() + return True + else: + return False + def get_toplevel(self): return hippo.get_canvas_for_item(self._item).get_toplevel() diff --git a/src/sugar/util.py b/src/sugar/util.py index 12f6824..8f81210 100644 --- a/src/sugar/util.py +++ b/src/sugar/util.py @@ -21,6 +21,8 @@ import sha import random import binascii import string +from gettext import gettext as _ +import gettext def printable_hash(in_hash): """Convert binary hash data into printable characters.""" @@ -168,3 +170,71 @@ class LRU: 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], + ['%d minute', '%d minutes', 60]] + +AND = _(' and ') +COMMA = _(', ') + +# TRANS: Indicating something that just happened, eg. "just now", "moments ago" +NOW = _('Seconds ago') + +# TRANS: Indicating time passed, eg. "[10 day, 5 hours] ago", +# "[2 minutes] in the past", or "[3 years, 1 month] earlier" +ELAPSED = _('%s ago') + +# Explanation of the following hack: +# The xgettext utility extracts plural forms by reading the strings included as +# parameters of ngettext(). As our plurals are not passed to ngettext() +# straight away because there needs to be a calculation before we know which +# 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 minute', '%d minutes', 1) + +del ngettext + +# End of plurals hack + +def timestamp_to_elapsed_string(timestamp, max_levels=2): + levels = 0 + time_period = '' + elapsed_seconds = int(time.time() - timestamp) + + for name_singular, name_plural, factor in units: + elapsed_units = elapsed_seconds / factor + if elapsed_units > 0: + + if levels > 0: + time_period += COMMA + + time_period += gettext.ngettext(name_singular, name_plural, + elapsed_units) % elapsed_units + + elapsed_seconds -= elapsed_units * factor + + if time_period != '': + levels += 1 + + if levels == max_levels: + break + + if levels == 0: + return NOW + + return ELAPSED % time_period + |