diff --git a/lib/src/sugar-utils.c b/lib/src/sugar-utils.c index f7bf1c5a..6cc59462 100644 --- a/lib/src/sugar-utils.c +++ b/lib/src/sugar-utils.c @@ -19,19 +19,71 @@ #include #include +#include +#include #include "sugar-utils.h" +/* Ported from mozilla nsDeviceContextGTK.cpp */ + +static gint +get_gtk_settings_dpi(void) +{ + GtkSettings *settings = gtk_settings_get_default(); + GParamSpec *spec; + gint dpi = 0; + + spec = g_object_class_find_property( + G_OBJECT_GET_CLASS(G_OBJECT(settings)), "gtk-xft-dpi"); + + if (spec) { + g_object_get(G_OBJECT(settings), + "gtk-xft-dpi", &dpi, + NULL); + } + + return (int)(dpi / 1024.0 + 0.5); +} + +static gint +get_xft_dpi(void) +{ + char *val = XGetDefault(GDK_DISPLAY(), "Xft", "dpi"); + if (val) { + char *e; + double d = strtod(val, &e); + + if (e != val) + return (int)(d + 0.5); + } + + return 0; +} + +static int +get_dpi_from_physical_resolution(void) +{ + float screen_width_in; + + screen_width_in = (float)(gdk_screen_width_mm()) / 25.4f; + + return (int)((float)(gdk_screen_width()) / screen_width_in + 0.5); +} + gint sugar_get_screen_dpi(void) { - char *val = XGetDefault (GDK_DISPLAY(), "Xft", "dpi"); - if (val) { - char *e; - double d = strtod(val, &e); - if (d > 0.0) - return (int)(d+0.5); + int dpi; + + dpi = get_gtk_settings_dpi(); + + if (dpi == 0) { + dpi = get_xft_dpi(); } - return 96; + if (dpi == 0) { + dpi = get_dpi_from_physical_resolution(); + } + + return dpi; } diff --git a/shell/view/clipboardicon.py b/shell/view/clipboardicon.py index caa949ad..c8c76922 100644 --- a/shell/view/clipboardicon.py +++ b/shell/view/clipboardicon.py @@ -47,13 +47,6 @@ class ClipboardIcon(CanvasIcon): self.props.xo_color = XoColor("#000000,#424242") else: self.props.xo_color = XoColor("#000000,#FFFFFF") - - def _activity_create_success_cb(self, handler, activity): - activity.start(util.unique_id()) - activity.execute("open_document", [self._object_id]) - - def _activity_create_error_cb(self, handler, err): - pass def _open_file(self): if self._percent < 100 or not self._activity: @@ -61,10 +54,7 @@ class ClipboardIcon(CanvasIcon): logging.debug("_icon_activated_cb: " + self._object_id) - # Launch the activity to handle this item - handler = activityfactory.create(self._activity) - handler.connect('success', self._activity_create_success_cb) - handler.connect('error', self._activity_create_error_cb) + activityfactory.create_with_uri(self._activity, self._object_id) def _icon_activated_cb(self, icon): self._open_file() diff --git a/shell/view/devices/battery.py b/shell/view/devices/battery.py index a13304e2..520f94c2 100644 --- a/shell/view/devices/battery.py +++ b/shell/view/devices/battery.py @@ -1,6 +1,11 @@ -from view.devices import deviceview +from sugar.graphics import canvasicon -class DeviceView(deviceview.DeviceView): +_ICON_NAME = 'device-battery' + +class DeviceView(canvasicon.CanvasIcon): def __init__(self, model): - deviceview.DeviceView.__init__(self, model) - self.props.icon_name = 'theme:stock-close' + canvasicon.CanvasIcon.__init__(self) + self._model = model + + icon_name = canvasicon.get_icon_state(_ICON_NAME, 60) + self.props.icon_name = icon_name diff --git a/shell/view/devices/deviceview.py b/shell/view/devices/deviceview.py index f35f0824..017fd58b 100644 --- a/shell/view/devices/deviceview.py +++ b/shell/view/devices/deviceview.py @@ -1,10 +1,5 @@ from sugar.graphics.canvasicon import CanvasIcon -class DeviceView(CanvasIcon): - def __init__(self, model): - CanvasIcon.__init__(self) - self.model = model - def create(model): name = 'view.devices.' + model.get_type() diff --git a/shell/view/devices/wirelessnetwork.py b/shell/view/devices/wirelessnetwork.py index 9164810c..d18fea06 100644 --- a/shell/view/devices/wirelessnetwork.py +++ b/shell/view/devices/wirelessnetwork.py @@ -15,20 +15,16 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -from view.devices import deviceview +from sugar.graphics import canvasicon +from sugar.graphics import color from model.devices import wirelessnetwork +from view.pulsingicon import PulsingIcon -_strength_to_icon = { - (0, 20) : 'stock-net-wireless-00', - (21, 40) : 'stock-net-wireless-21-40', - (41, 60) : 'stock-net-wireless-41-60', - (61, 80) : 'stock-net-wireless-61-80', - (81, 100) : 'stock-net-wireless-81-100' -} +_ICON_NAME = 'device-network-wireless' -class DeviceView(deviceview.DeviceView): +class DeviceView(PulsingIcon): def __init__(self, model): - deviceview.DeviceView.__init__(self, model) + PulsingIcon.__init__(self) self._model = model model.connect('notify::name', self._name_changed_cb) @@ -52,18 +48,20 @@ class DeviceView(deviceview.DeviceView): self.props.tooltip = self._model.props.name def _update_icon(self): - strength = self._model.props.strength - for interval in _strength_to_icon.keys(): - if strength >= interval[0] and strength <= interval[1]: - stock_name = _strength_to_icon[interval] - self.props.icon_name = 'theme:' + stock_name + icon_name = canvasicon.get_icon_state( + _ICON_NAME, self._model.props.strength) + if icon_name: + self.props.icon_name = icon_name def _update_state(self): # FIXME Change icon colors once we have real icons state = self._model.props.state if state == wirelessnetwork.STATE_ACTIVATING: - self.props.background_color = 0xFF0000FF + self.props.fill_color = color.ICON_FILL_INACTIVE + self.props.stroke_color = color.ICON_STROKE_INACTIVE elif state == wirelessnetwork.STATE_ACTIVATED: - self.props.background_color = 0x00FF00FF + self.props.fill_color = None + self.props.stroke_color = None elif state == wirelessnetwork.STATE_INACTIVE: - self.props.background_color = 0x00000000 + self.props.fill_color = color.ICON_FILL_INACTIVE + self.props.stroke_color = color.ICON_STROKE_INACTIVE diff --git a/shell/view/home/MeshBox.py b/shell/view/home/MeshBox.py index ad94bef1..c8d050c1 100644 --- a/shell/view/home/MeshBox.py +++ b/shell/view/home/MeshBox.py @@ -22,21 +22,18 @@ import gobject from sugar.graphics.spreadbox import SpreadBox from sugar.graphics.snowflakebox import SnowflakeBox from sugar.graphics.canvasicon import CanvasIcon +from sugar.graphics import color +from sugar.graphics import canvasicon from model import accesspointmodel from hardware import hardwaremanager from view.BuddyIcon import BuddyIcon +from view.pulsingicon import PulsingIcon -_strength_to_icon = { - (0, 20) : 'stock-net-wireless-00', - (21, 40) : 'stock-net-wireless-21-40', - (41, 60) : 'stock-net-wireless-41-60', - (61, 80) : 'stock-net-wireless-61-80', - (81, 100) : 'stock-net-wireless-81-100' -} +_ICON_NAME = 'device-network-wireless' -class AccessPointView(CanvasIcon): +class AccessPointView(PulsingIcon): def __init__(self, model): - CanvasIcon.__init__(self) + PulsingIcon.__init__(self) self._model = model self.connect('activated', self._activate_cb) @@ -45,6 +42,11 @@ class AccessPointView(CanvasIcon): model.connect('notify::name', self._name_changed_cb) model.connect('notify::state', self._state_changed_cb) + self.props.colors = [ + [ None, None ], + [ color.ICON_FILL_INACTIVE, color.ICON_STROKE_INACTIVE ] + ] + self._update_icon() self._update_name() self._update_state() @@ -69,20 +71,22 @@ class AccessPointView(CanvasIcon): self.props.tooltip = self._model.props.name def _update_icon(self): - strength = self._model.props.strength - for interval in _strength_to_icon.keys(): - if strength >= interval[0] and strength <= interval[1]: - stock_name = _strength_to_icon[interval] - self.props.icon_name = 'theme:' + stock_name + icon_name = canvasicon.get_icon_state( + _ICON_NAME, self._model.props.strength) + if icon_name: + self.props.icon_name = icon_name def _update_state(self): - # FIXME Change icon colors once we have real icons if self._model.props.state == accesspointmodel.STATE_CONNECTING: - self.props.background_color = 0xFF0000FF + self.props.pulsing = True elif self._model.props.state == accesspointmodel.STATE_CONNECTED: - self.props.background_color = 0x00FF00FF + self.props.pulsing = False + self.props.fill_color = None + self.props.stroke_color = None elif self._model.props.state == accesspointmodel.STATE_NOTCONNECTED: - self.props.background_color = 0x00000000 + self.props.pulsing = False + self.props.fill_color = color.ICON_FILL_INACTIVE + self.props.stroke_color = color.ICON_STROKE_INACTIVE class ActivityView(SnowflakeBox): def __init__(self, shell, menu_shell, model): diff --git a/shell/view/pulsingicon.py b/shell/view/pulsingicon.py new file mode 100644 index 00000000..d5eca199 --- /dev/null +++ b/shell/view/pulsingicon.py @@ -0,0 +1,78 @@ +# Copyright (C) 2006, Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import gobject + +from sugar.graphics.canvasicon import CanvasIcon + +class PulsingIcon(CanvasIcon): + __gproperties__ = { + 'colors' : (object, None, None, + gobject.PARAM_READWRITE), + 'pulsing' : (bool, None, None, False, + gobject.PARAM_READWRITE) + } + + def __init__(self, **kwargs): + self._pulsing = False + self._colors = None + self._pulse_sid = 0 + self._pos = 0 + + CanvasIcon.__init__(self, **kwargs) + + def do_set_property(self, pspec, value): + CanvasIcon.do_set_property(self, pspec, value) + + if pspec.name == 'pulsing': + self._pulsing = value + if self._pulsing: + self._start() + else: + self._stop() + elif pspec.name == 'colors': + self._colors = value + self._pos = 0 + + def do_get_property(self, pspec): + CanvasIcon.do_get_property(self, pspec, value) + + if pspec.name == 'pulsing': + return self._pulsing + elif pspec.name == 'colors': + return self._colors + + def _pulse_timeout(self): + if not self._colors: + return + + self.props.stroke_color = self._colors[self._pos][0] + self.props.fill_color = self._colors[self._pos][1] + + self._pos += 1 + if self._pos == len(self._colors): + self._pos = 0 + + return True + + def _start(self): + if self._pulse_sid == 0: + self._pulse_sid = gobject.timeout_add(1000, self._pulse_timeout) + + def _stop(self): + if self._pulse_sid: + gobject.source_remove(self._pulse_sid) + self._pulse_sid = 0 diff --git a/sugar-emulator b/sugar-emulator index a163fce9..f951649a 100755 --- a/sugar-emulator +++ b/sugar-emulator @@ -36,6 +36,7 @@ else: from sugar import env from sugar import util from sugar.emulator import Emulator +import _sugar if len(sys.argv) == 1: program = 'sugar-shell' @@ -51,7 +52,9 @@ else: width = 1200 height = 900 -emulator = Emulator(width, height, fullscreen) +dpi = min(_sugar.get_screen_dpi(), 96) + +emulator = Emulator(width, height, fullscreen, dpi) emulator.start() if sourcedir: diff --git a/sugar/emulator.py b/sugar/emulator.py index 6cc4f52e..8491dbfd 100644 --- a/sugar/emulator.py +++ b/sugar/emulator.py @@ -78,7 +78,7 @@ class MatchboxProcess(Process): return 'Matchbox' class XephyrProcess(Process): - def __init__(self, width, height, fullscreen): + def __init__(self, width, height, fullscreen, dpi): self._display = get_display_number() cmd = 'Xephyr :%d -ac ' % (self._display) @@ -88,6 +88,9 @@ class XephyrProcess(Process): if width > 0 and height > 0: cmd += ' -screen %dx%d' % (width, height) + if dpi > 0: + cmd += ' -dpi %d' % (dpi) + Process.__init__(self, cmd) def get_name(self): @@ -100,15 +103,16 @@ class XephyrProcess(Process): class Emulator(object): """The OLPC emulator""" - def __init__(self, width, height, fullscreen): + def __init__(self, width, height, fullscreen, dpi): self._fullscreen = fullscreen self._width = width self._height = height + self._dpi = dpi def start(self): try: process = XephyrProcess(self._width, self._height, - self._fullscreen) + self._fullscreen, self._dpi) process.start() except: print 'Cannot run the emulator. You need to install Xephyr' diff --git a/sugar/graphics/canvasicon.py b/sugar/graphics/canvasicon.py index 56423d8e..4b1134c6 100644 --- a/sugar/graphics/canvasicon.py +++ b/sugar/graphics/canvasicon.py @@ -415,3 +415,15 @@ class CanvasIcon(hippo.CanvasBox, hippo.CanvasItem): Override this method for adding prelighting behavior. """ pass + +def get_icon_state(base_name, perc): + step = 5 + strength = round(perc / step) * step + icon_theme = gtk.icon_theme_get_default() + + while strength <= 100: + icon_name = '%s-%03d' % (base_name, strength) + if icon_theme.has_icon(icon_name): + return 'theme:' + icon_name + + strength = strength + step diff --git a/sugar/graphics/color.py b/sugar/graphics/color.py index 864e8e39..5ff4c928 100644 --- a/sugar/graphics/color.py +++ b/sugar/graphics/color.py @@ -21,7 +21,9 @@ _system_colors = { 'button-hover' : '#808080', 'button-background-hover' : '#000000', 'button-inactive' : '#808080', - 'button-background-inactive' : '#424242' + 'button-background-inactive' : '#424242', + 'icon-stroke-inactive' : '#757575', + 'icon-fill-inactive' : '#9D9FA1' } def _html_to_rgb(html_color): @@ -100,3 +102,5 @@ BUTTON_HOVER = SystemColor('button-hover') BUTTON_BACKGROUND_HOVER = SystemColor('button-background-hover') BUTTON_INACTIVE = SystemColor('button-inactive') BUTTON_BACKGROUND_INACTIVE = SystemColor('button-background-inactive') +ICON_FILL_INACTIVE = SystemColor('icon-fill-inactive') +ICON_STROKE_INACTIVE = SystemColor('icon-stroke-inactive') diff --git a/sugar/graphics/entry.py b/sugar/graphics/entry.py index def08c9e..40604bb4 100644 --- a/sugar/graphics/entry.py +++ b/sugar/graphics/entry.py @@ -39,31 +39,33 @@ class Entry(hippo.CanvasBox, hippo.CanvasItem): 'button-activated': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([int])) } - def __init__(self, text=''): - hippo.CanvasBox.__init__(self, orientation=hippo.ORIENTATION_HORIZONTAL) - self.props.yalign = hippo.ALIGNMENT_CENTER - - self._buttons = {} - - self._round_box = RoundBox() - self._round_box.props.border_color = color.FRAME_BORDER.get_int() - self.append(self._round_box, hippo.PACK_EXPAND) - + def __init__(self, **kwargs): self._entry = self.create_entry() + self._entry.props.text = '' self._entry.props.has_frame = False - self._entry.props.text = text - self._update_colors(focused=False) self._entry.modify_text(gtk.STATE_SELECTED, color.BLACK.get_gdk_color()) self._entry.connect('focus-in-event', self._entry_focus_in_event_cb) self._entry.connect('focus-out-event', self._entry_focus_out_event_cb) self._entry.connect('activate', self._entry_activate_cb) self._entry.modify_font(font.DEFAULT.get_pango_desc()) - + + hippo.CanvasBox.__init__(self, **kwargs) + self.props.orientation = hippo.ORIENTATION_HORIZONTAL + self.props.yalign = hippo.ALIGNMENT_CENTER + + self._buttons = {} + + self._round_box = RoundBox() + self._round_box.props.border_color = color.FRAME_BORDER.get_int() + self.append(self._round_box, hippo.PACK_EXPAND) + self._canvas_widget = hippo.CanvasWidget() self._canvas_widget.props.widget = self._entry self._round_box.append(self._canvas_widget, hippo.PACK_EXPAND) + self._update_colors(focused=False) + def create_entry(self): """ Subclasses can override this method in order to provide a different @@ -84,10 +86,16 @@ class Entry(hippo.CanvasBox, hippo.CanvasItem): self._buttons[button] = action_id def do_set_property(self, pspec, value): - self._entry.set_property(pspec.name, value) + if pspec.name in [param.name for param in self._entry.props]: + self._entry.set_property(pspec.name, value) + else: + hippo.CanvasBox.set_property(self, pspec.name, value) def do_get_property(self, pspec): - return self._entry.get_property(pspec.name) + if pspec.name in [param.name for param in self._entry.props]: + return self._entry.get_property(pspec.name) + else: + return hippo.CanvasBox.get_property(self, pspec.name) def _entry_focus_in_event_cb(self, widget, event): self._update_colors(focused=True) diff --git a/sugar/graphics/font.py b/sugar/graphics/font.py index aa4f2dcb..bf532694 100644 --- a/sugar/graphics/font.py +++ b/sugar/graphics/font.py @@ -3,8 +3,8 @@ import pango from sugar.graphics import units _system_fonts = { - 'default' : 'Bitstream Vera Sans %d' % units.points_to_pixels(9), - 'default-bold' : 'Bitstream Vera Sans bold %d' % units.points_to_pixels(9) + 'default' : 'Bitstream Vera Sans %dpx' % units.points_to_pixels(9), + 'default-bold' : 'Bitstream Vera Sans bold %dpx' % units.points_to_pixels(9) } class Font(object): diff --git a/sugar/graphics/iconbutton.py b/sugar/graphics/iconbutton.py index a58300b0..fea02fbd 100644 --- a/sugar/graphics/iconbutton.py +++ b/sugar/graphics/iconbutton.py @@ -43,16 +43,17 @@ class IconButton(CanvasIcon): CanvasIcon.__init__(self, cache=True, **kwargs) - if not self.props.fill_color: + if self._active: self.props.fill_color = color.BUTTON_BACKGROUND_NORMAL - if not self.props.stroke_color: self.props.stroke_color = color.BUTTON_NORMAL - self.props.background_color = color.BUTTON_BACKGROUND_NORMAL.get_int() + self.props.background_color = \ + color.BUTTON_BACKGROUND_NORMAL.get_int() + else: + self.props.fill_color = color.BUTTON_BACKGROUND_INACTIVE + self.props.stroke_color = color.BUTTON_INACTIVE + self.props.background_color = \ + color.BUTTON_BACKGROUND_INACTIVE.get_int() - self._normal_fill_color = self.props.fill_color - self._normal_stroke_color = self.props.stroke_color - self._normal_background_color = self.props.background_color - self._set_size(STANDARD_SIZE) self.connect('button-press-event', @@ -76,13 +77,11 @@ class IconButton(CanvasIcon): elif pspec.name == 'active': self._active = value if self._active: - self.props.fill_color = self._normal_fill_color - self.props.stroke_color = self._normal_stroke_color - self.props.background_color = self._normal_background_color + self.props.fill_color = color.BUTTON_BACKGROUND_NORMAL + self.props.stroke_color = color.BUTTON_NORMAL else: self.props.fill_color = color.BUTTON_BACKGROUND_INACTIVE self.props.stroke_color = color.BUTTON_INACTIVE - self.props.background_color = color.BUTTON_BACKGROUND_INACTIVE.get_int() else: CanvasIcon.do_set_property(self, pspec, value) @@ -104,5 +103,5 @@ class IconButton(CanvasIcon): if self._active: self.props.background_color = color.BLACK.get_int() else: - if self._active: - self.props.background_color = self._normal_background_color + self.props.background_color = \ + color.BUTTON_BACKGROUND_NORMAL.get_int() diff --git a/tools/build-snapshot.sh b/tools/build-snapshot.sh index 1fe49888..5a937631 100755 --- a/tools/build-snapshot.sh +++ b/tools/build-snapshot.sh @@ -1,6 +1,6 @@ VERSION=0.63 DATE=`date +%Y%m%d` -RELEASE=2.23 +RELEASE=2.25 TARBALL=sugar-$VERSION-$RELEASE.${DATE}git.tar.bz2 rm sugar-$VERSION.tar.bz2