Merge branch 'master' of git+ssh://dev.laptop.org/git/sugar

This commit is contained in:
Simon McVittie 2007-05-30 17:43:31 +01:00
commit cd12b59967
12 changed files with 327 additions and 75 deletions

4
.gitignore vendored
View File

@ -10,6 +10,7 @@ Makefile.in
*.lo *.lo
*.loT *.loT
.*.sw? .*.sw?
*.service
# Absolute # Absolute
@ -51,9 +52,6 @@ browser/sugar-marshal.c
browser/sugar-marshal.h browser/sugar-marshal.h
browser/stamp-sugar-marshal.c browser/stamp-sugar-marshal.c
browser/stamp-sugar-marshal.h browser/stamp-sugar-marshal.h
services/clipboard/org.laptop.Clipboard.service
services/console/org.laptop.sugar.Console.service
services/presence/org.laptop.Sugar.Presence.service
bin/sugar bin/sugar
shell/extensions/_extensions.c shell/extensions/_extensions.c
data/sugar.gtkrc data/sugar.gtkrc

View File

@ -1,12 +1,10 @@
VERSION=0.63 VERSION=0.64
DATE=`date +%Y%m%d` ALPHATAG=`git-show-ref --hash=10 refs/heads/master`
RELEASE=2.87 TARBALL=sugar-$VERSION-git$ALPHATAG.tar.bz2
TARBALL=sugar-$VERSION-$RELEASE.${DATE}git.tar.bz2
rm sugar-$VERSION.tar.bz2 rm sugar-$VERSION.tar.bz2
XUL_SDK=/home/marco/sugar-jhbuild/build/lib/xulrunner-1.9a5pre-dev make distcheck
DISTCHECK_CONFIGURE_FLAGS="--with-libxul-sdk=$XUL_SDK" make distcheck
mv sugar-$VERSION.tar.bz2 $TARBALL mv sugar-$VERSION.tar.bz2 $TARBALL
scp $TARBALL mpg@devserv.devel.redhat.com:~ scp $TARBALL mpg@devserv.devel.redhat.com:~

View File

@ -1,4 +1,4 @@
AC_INIT([Sugar],[0.63],[],[sugar]) AC_INIT([Sugar],[0.64],[],[sugar])
AC_PREREQ([2.59]) AC_PREREQ([2.59])

View File

@ -1,18 +1,23 @@
servicedir = $(datadir)/dbus-1/services servicedir = $(datadir)/dbus-1/services
service_in_files = org.laptop.Clipboard.service.in
service_in_files = \
org.laptop.Clipboard.service.in \
org.laptop.ObjectTypeRegistry.service.in
service_DATA = $(service_in_files:.service.in=.service) service_DATA = $(service_in_files:.service.in=.service)
$(service_DATA): $(service_in_files) Makefile $(service_DATA): $(service_in_files) Makefile
@sed -e "s|\@bindir\@|$(bindir)|" $< > $@ @sed -e "s|\@bindir\@|$(bindir)|" $< > $@
sugardir = $(pkgdatadir)/services/clipboard sugardir = $(pkgdatadir)/services/clipboard
sugar_PYTHON = \ sugar_PYTHON = \
__init__.py \ __init__.py \
clipboardobject.py \ clipboardobject.py \
clipboardservice.py \ clipboardservice.py \
objecttypeservice.py \
typeregistry.py typeregistry.py
bin_SCRIPTS = sugar-clipboard bin_SCRIPTS = sugar-clipboard
DISTCLEANFILES = $(service_DATA) DISTCLEANFILES = $(service_DATA)

View File

@ -0,0 +1,62 @@
# Copyright (C) 2007, 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 dbus
import dbus.service
from sugar.objects.objecttype import ObjectType
_REGISTRY_IFACE = "org.laptop.ObjectTypeRegistry"
_REGISTRY_PATH = "/org/laptop/ObjectTypeRegistry"
class ObjectTypeRegistry(dbus.service.Object):
def __init__(self):
bus = dbus.SessionBus()
bus_name = dbus.service.BusName(self._REGISTRY_IFACE, bus=bus)
dbus.service.Object.__init__(self, bus_name, self._REGISTRY_PATH)
self._types = {}
self._add_primitive('Text', _('Text'), 'object-text',
[ 'text/rtf' ])
self._add_primitive('Image', _('Image'), 'object-image',
[ 'image/png' ])
def _add_primitive(self, type_id, name, icon, mime_types):
object_type = ObjectType(type_id, name, icon, mime_types)
self._types.add(object_type)
def _get_type_for_mime(self, mime_type):
for object_type in self._types.values():
if mime_type in object_type.mime_types:
return object_type
@dbus.service.method(_CLIPBOARD_DBUS_INTERFACE,
in_signature="s", out_signature="a{sv}")
def GetType(self, type_id):
if self._types.has_key(type_id):
return self._types[type_id].to_dict()
else:
return []
@dbus.service.method(_CLIPBOARD_DBUS_INTERFACE,
in_signature="s", out_signature="a{sv}")
def GetTypeForMIME(self, mime_type):
object_type = self._get_type_for_mime(mime_type)
if object_type:
return object_type.to_dict()
else:
return []

View File

@ -0,0 +1,4 @@
[D-BUS Service]
Name = org.laptop.ObjectTypeRegistry
Exec = @bindir@/sugar-clipboard

View File

@ -42,14 +42,14 @@ class ActivityToolbar(gtk.Toolbar):
activity.connect('shared', self._activity_shared_cb) activity.connect('shared', self._activity_shared_cb)
activity.connect('joined', self._activity_shared_cb) activity.connect('joined', self._activity_shared_cb)
if activity.jobject: if activity.metadata:
self.title = gtk.Entry() self.title = gtk.Entry()
self.title.set_size_request(int(gtk.gdk.screen_width() / 6), -1) self.title.set_size_request(int(gtk.gdk.screen_width() / 6), -1)
self.title.set_text(activity.jobject['title']) self.title.set_text(activity.metadata['title'])
self.title.connect('focus-out-event', self._title_focus_out_event_cb) self.title.connect('focus-out-event', self._title_focus_out_event_cb)
self._add_widget(self.title) self._add_widget(self.title)
activity.jobject.connect('updated', self._jobject_updated_cb) activity.metadata.connect('updated', self._jobject_updated_cb)
separator = gtk.SeparatorToolItem() separator = gtk.SeparatorToolItem()
separator.props.draw = False separator.props.draw = False
@ -84,8 +84,8 @@ class ActivityToolbar(gtk.Toolbar):
self.title.set_text(jobject['title']) self.title.set_text(jobject['title'])
def _title_focus_out_event_cb(self, entry, event): def _title_focus_out_event_cb(self, entry, event):
if self._activity.jobject['title'] != self.title.get_text(): if self._activity.metadata['title'] != self.title.get_text():
self._activity.jobject['title'] = self.title.get_text() self._activity.metadata['title'] = self.title.get_text()
self._activity.save() self._activity.save()
def _add_widget(self, widget, expand=False): def _add_widget(self, widget, expand=False):
@ -199,54 +199,62 @@ class Activity(Window, gtk.Container):
self._bus = ActivityService(self) self._bus = ActivityService(self)
if handle.object_id: if handle.object_id:
self.jobject = datastore.get(handle.object_id) self._jobject = datastore.get(handle.object_id)
self.jobject.object_id = '' self._jobject.object_id = ''
del self.jobject['ctime'] del self._jobject.metadata['ctime']
del self.jobject['mtime'] del self._jobject.metadata['mtime']
elif create_jobject: elif create_jobject:
logging.debug('Creating a jobject.') logging.debug('Creating a jobject.')
self.jobject = datastore.create() self._jobject = datastore.create()
self.jobject['title'] = '%s %s' % (get_bundle_name(), 'Activity') self._jobject.metadata['title'] = '%s %s' % (get_bundle_name(), 'Activity')
self.jobject['activity'] = self.get_service_name() self._jobject.metadata['activity'] = self.get_service_name()
self.jobject['keep'] = '0' self._jobject.metadata['keep'] = '0'
self.jobject['buddies'] = '' self._jobject.metadata['buddies'] = ''
self.jobject['preview'] = '' self._jobject.metadata['preview'] = ''
self.jobject['icon-color'] = profile.get_color().to_string() self._jobject.metadata['icon-color'] = profile.get_color().to_string()
self.jobject.file_path = '' self._jobject.file_path = ''
datastore.write(self.jobject, datastore.write(self._jobject,
reply_handler=self._internal_jobject_create_cb, reply_handler=self._internal_jobject_create_cb,
error_handler=self._internal_jobject_error_cb) error_handler=self._internal_jobject_error_cb)
else: else:
self.jobject = None self._jobject = None
def do_set_property(self, pspec, value): def do_set_property(self, pspec, value):
if pspec.name == 'active': if pspec.name == 'active':
if self._active != value: if self._active != value:
self._active = value self._active = value
if not self._active and self.jobject: if not self._active and self._jobject:
self.save() self.save()
def do_get_property(self, pspec): def do_get_property(self, pspec):
if pspec.name == 'active': if pspec.name == 'active':
return self._active return self._active
def set_canvas(self, canvas):
Window.set_canvas(self, canvas)
canvas.connect('map', self._canvas_map_cb)
def _canvas_map_cb(self, canvas):
if self._jobject and self._jobject.file_path:
self.read_file(self._jobject.file_path)
def _internal_jobject_create_cb(self): def _internal_jobject_create_cb(self):
pass pass
def _internal_jobject_error_cb(self, err): def _internal_jobject_error_cb(self, err):
logging.debug("Error creating activity datastore object: %s" % err) logging.debug("Error creating activity datastore object: %s" % err)
def read_file(self): def read_file(self, file_path):
""" """
Subclasses implement this method if they support resuming objects from Subclasses implement this method if they support resuming objects from
the journal. Can access the object through the jobject attribute. the journal. 'file_path' is the file to read from.
""" """
raise NotImplementedError raise NotImplementedError
def write_file(self): def write_file(self, file_path):
""" """
Subclasses implement this method if they support saving data to objects Subclasses implement this method if they support saving data to objects
in the journal. Can access the object through the jobject attribute. in the journal. 'file_path' is the file to write to.
""" """
raise NotImplementedError raise NotImplementedError
@ -259,11 +267,12 @@ class Activity(Window, gtk.Container):
def save(self): def save(self):
"""Request that the activity is saved to the Journal.""" """Request that the activity is saved to the Journal."""
try: try:
self.jobject.file_path = os.path.join('/tmp', '%i.txt' % time.time()) file_path = os.path.join('/tmp', '%i' % time.time())
self.write_file() self.write_file(file_path)
self._jobject.file_path = file_path
except NotImplementedError: except NotImplementedError:
self.jobject.file_path = '' pass
datastore.write(self.jobject, datastore.write(self._jobject,
reply_handler=self._internal_save_cb, reply_handler=self._internal_save_cb,
error_handler=self._internal_save_error_cb) error_handler=self._internal_save_error_cb)
@ -322,7 +331,7 @@ class Activity(Window, gtk.Container):
self._shared_activity.leave() self._shared_activity.leave()
def close(self): def close(self):
if self.jobject: if self._jobject:
try: try:
self.save() self.save()
except: except:
@ -330,6 +339,14 @@ class Activity(Window, gtk.Container):
raise raise
self.destroy() self.destroy()
def get_metadata(self):
if self._jobject:
return self._jobject.metadata
else:
return None
metadata = property(get_metadata, None)
def get_bundle_name(): def get_bundle_name():
"""Return the bundle name for the current process' bundle """Return the bundle name for the current process' bundle
""" """

View File

@ -19,38 +19,48 @@ import gobject
from sugar.datastore import dbus_helpers from sugar.datastore import dbus_helpers
class DSObject(gobject.GObject): class DSMetadata(gobject.GObject):
__gsignals__ = { __gsignals__ = {
'updated': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, 'updated': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
([])) ([]))
} }
def __init__(self, object_id, metadata=None, file_path=None): def __init__(self, props={}):
gobject.GObject.__init__(self) gobject.GObject.__init__(self)
self._props = props
def __getitem__(self, key):
return self._props[key]
def __setitem__(self, key, value):
if not self._props.has_key(key) or self._props[key] != value:
self._props[key] = value
self.emit('updated')
def __delitem__(self, key):
del self._props[key]
def has_key(self, key):
return self._props.has_key(key)
def get_dictionary(self):
return self._props
class DSObject:
def __init__(self, object_id, metadata=None, file_path=None):
self.object_id = object_id self.object_id = object_id
self._metadata = metadata self._metadata = metadata
self._file_path = file_path self._file_path = file_path
def __getitem__(self, key):
return self.metadata[key]
def __setitem__(self, key, value):
if not self.metadata.has_key(key) or self.metadata[key] != value:
self.metadata[key] = value
self.emit('updated')
def __delitem__(self, key):
del self.metadata[key]
def get_metadata(self): def get_metadata(self):
if self._metadata is None and not self.object_id is None: if self._metadata is None and not self.object_id is None:
self.set_metadata(dbus_helpers.get_properties(self.object_id)) metadata = DSMetadata(dbus_helpers.get_properties(self.object_id))
self._metadata = metadata
return self._metadata return self._metadata
def set_metadata(self, metadata): def set_metadata(self, metadata):
if self._metadata != metadata: if self._metadata != metadata:
self._metadata = metadata self._metadata = metadata
self.emit('updated')
metadata = property(get_metadata, set_metadata) metadata = property(get_metadata, set_metadata)
@ -62,7 +72,6 @@ class DSObject(gobject.GObject):
def set_file_path(self, file_path): def set_file_path(self, file_path):
if self._file_path != file_path: if self._file_path != file_path:
self._file_path = file_path self._file_path = file_path
self.emit('updated')
file_path = property(get_file_path, set_file_path) file_path = property(get_file_path, set_file_path)
@ -71,23 +80,23 @@ def get(object_id):
metadata = dbus_helpers.get_properties(object_id) metadata = dbus_helpers.get_properties(object_id)
file_path = dbus_helpers.get_filename(object_id) file_path = dbus_helpers.get_filename(object_id)
ds_object = DSObject(object_id, metadata, file_path) ds_object = DSObject(object_id, DSMetadata(metadata), file_path)
# TODO: register the object for updates # TODO: register the object for updates
return ds_object return ds_object
def create(): def create():
return DSObject(object_id=None, metadata={}, file_path=None) return DSObject(object_id=None, metadata=DSMetadata(), file_path=None)
def write(ds_object, reply_handler=None, error_handler=None): def write(ds_object, reply_handler=None, error_handler=None):
logging.debug('datastore.write') logging.debug('datastore.write')
if ds_object.object_id: if ds_object.object_id:
dbus_helpers.update(ds_object.object_id, dbus_helpers.update(ds_object.object_id,
ds_object.metadata, ds_object.metadata.get_dictionary(),
ds_object.file_path, ds_object.file_path,
reply_handler=reply_handler, reply_handler=reply_handler,
error_handler=error_handler) error_handler=error_handler)
else: else:
ds_object.object_id = dbus_helpers.create(ds_object.metadata, ds_object.object_id = dbus_helpers.create(ds_object.metadata.get_dictionary(),
ds_object.file_path) ds_object.file_path)
# TODO: register the object for updates # TODO: register the object for updates
logging.debug('Written object %s to the datastore.' % ds_object.object_id) logging.debug('Written object %s to the datastore.' % ds_object.object_id)
@ -114,7 +123,7 @@ def find(query, sorting=None, limit=None, offset=None, reply_handler=None,
object_id = props['uid'] object_id = props['uid']
del props['uid'] del props['uid']
ds_object = DSObject(object_id, props, file_path) ds_object = DSObject(object_id, DSMetadata(props), file_path)
objects.append(ds_object) objects.append(ds_object)
return objects, total_count return objects, total_count

View File

@ -17,6 +17,7 @@
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import gtk import gtk
from gtk import gdk, keysyms
import gobject import gobject
import pango import pango
@ -49,6 +50,7 @@ class Palette(gtk.Window):
self._palette_label = gtk.Label() self._palette_label = gtk.Label()
self._palette_label.set_ellipsize(pango.ELLIPSIZE_START) self._palette_label.set_ellipsize(pango.ELLIPSIZE_START)
self._palette_label.show()
self._separator = gtk.HSeparator() self._separator = gtk.HSeparator()
self._separator.hide() self._separator.hide()
@ -58,7 +60,10 @@ class Palette(gtk.Window):
self._menu_bar.show() self._menu_bar.show()
self._content = gtk.HBox() self._content = gtk.HBox()
self._content.show()
self._button_bar = gtk.HButtonBox() self._button_bar = gtk.HButtonBox()
self._button_bar.show()
# Set main container # Set main container
vbox = gtk.VBox(False, 0) vbox = gtk.VBox(False, 0)
@ -69,12 +74,26 @@ class Palette(gtk.Window):
vbox.pack_start(self._button_bar, True, True, self._PADDING) vbox.pack_start(self._button_bar, True, True, self._PADDING)
vbox.show() vbox.show()
# FIXME # Widget events
self.connect('focus_out_event', self._close_palette) self.connect('motion-notify-event', self._mouse_over_widget)
self.connect('leave-notify-event', self._mouse_out_widget)
self.connect('button-press-event', self._close_palette)
self.connect('key-press-event', self._on_key_press_event)
self.set_border_width(self._WIN_BORDER) self.set_border_width(self._WIN_BORDER)
self.add(vbox) self.add(vbox)
def _is_mouse_out(self, window, event):
# If we're clicking outside of the Palette
# return True
if (event.window != self.window or
(tuple(self.allocation.intersect(
gdk.Rectangle(x=int(event.x), y=int(event.y),
width=1, height=1)))) == (0, 0, 0, 0)):
return True
else:
return False
def do_set_property(self, pspec, value): def do_set_property(self, pspec, value):
if pspec.name == 'parent': if pspec.name == 'parent':
@ -126,8 +145,9 @@ class Palette(gtk.Window):
self.move(move_x, move_y) self.move(move_x, move_y)
def _close_palette(self, widget, event): def _close_palette(self, widget=None, event=None):
self.destroy() gtk.gdk.pointer_ungrab()
self.hide()
def set_primary_state(self, label, accel_path=None): def set_primary_state(self, label, accel_path=None):
if accel_path != None: if accel_path != None:
@ -148,9 +168,42 @@ class Palette(gtk.Window):
widget.show() widget.show()
def append_button(self, button): def append_button(self, button):
button.connect('released', self._close_palette)
self._button_bar.pack_start(button, True, True, self._PADDING) self._button_bar.pack_start(button, True, True, self._PADDING)
button.show() button.show()
def display(self, button): def display(self, button):
self.show() self.show()
self.set_position() self.set_position()
self._pointer_grab()
def _pointer_grab(self):
gtk.gdk.pointer_grab(self.window, owner_events=False,
event_mask=gtk.gdk.BUTTON_PRESS_MASK |
gtk.gdk.BUTTON_RELEASE_MASK |
gtk.gdk.ENTER_NOTIFY_MASK |
gtk.gdk.LEAVE_NOTIFY_MASK |
gtk.gdk.POINTER_MOTION_MASK)
gdk.keyboard_grab(self.window, False)
def _mouse_out_widget(self, widget, event):
if (widget == self) and self._is_mouse_out(widget, event):
self._pointer_grab()
def _mouse_over_widget(self, widget, event):
gtk.gdk.pointer_ungrab()
def _on_key_press_event(self, window, event):
# Escape or Alt+Up: Close
# Enter, Return or Space: Select
keyval = event.keyval
state = event.state & gtk.accelerator_get_default_mod_mask()
if (keyval == keysyms.Escape or
((keyval == keysyms.Up or keyval == keysyms.KP_Up) and
state == gdk.MOD1_MASK)):
self._close_palette()
elif keyval == keysyms.Tab:
self._close_palette()

View File

@ -36,4 +36,5 @@ class ToolButton(gtk.ToolButton):
palette.props.alignment = ALIGNMENT_BOTTOM_LEFT palette.props.alignment = ALIGNMENT_BOTTOM_LEFT
def set_tooltip(self, text): def set_tooltip(self, text):
pass tp = gtk.Tooltips()
self.set_tooltip(tp, text, text)

View File

@ -21,6 +21,7 @@ import os
import logging import logging
import traceback import traceback
from cStringIO import StringIO from cStringIO import StringIO
import time
from sugar import env from sugar import env
@ -114,5 +115,66 @@ def start(module_id):
def cleanup(): def cleanup():
logs_dir = _get_logs_dir() logs_dir = _get_logs_dir()
for f in os.listdir(logs_dir):
os.remove(os.path.join(logs_dir, f)) # File extension for backed up logfiles.
file_suffix = int(time.time())
# Absolute directory path where to store old logfiles.
# It will be created recursivly if it's not present.
backup_dirpath = os.path.join(logs_dir, 'old')
# How many versions shall be backed up of every logfile?
num_backup_versions = 4
# Make sure the backup location for old log files exists
if not os.path.exists(backup_dirpath):
os.makedirs(backup_dirpath)
# Iterate over every item in 'logs' directory
for filename in os.listdir(logs_dir):
old_filepath = os.path.join(logs_dir, filename)
if os.path.isfile(old_filepath):
# Backup every file
new_filename = filename + '.' + str(file_suffix)
new_filepath = os.path.join(backup_dirpath, new_filename)
os.rename(old_filepath, new_filepath)
backup_map = {}
# Temporarily map all backup logfiles
for filename in os.listdir(backup_dirpath):
# Remove the 'file_suffix' from the filename.
end = filename.rfind(".")
key = filename[0:end].lower()
key = key.replace(".", "_")
if key not in backup_map:
backup_map[key] = []
backup_list = backup_map[key]
backup_list.append( os.path.join(backup_dirpath, filename) )
# Only keep 'num_backup_versions' versions of every logfile.
# Remove the others.
for key in backup_map:
backup_list = backup_map[key]
backup_list.sort()
backup_list.reverse()
for i in range(num_backup_versions, len(backup_list)):
os.remove(backup_list[i])

View File

@ -0,0 +1,43 @@
_SERVICE = "org.laptop.ObjectTypeRegistry"
_PATH = "/org/laptop/ObjectTypeRegistry"
_IFACE = "org.laptop.ObjectTypeRegistry"
def _object_type_from_dict(info_dict):
if info_dict:
return ObjectType(info_dict['type_id'],
info_dict['name'],
info_dict['icon'])
else:
return None
class ObjectType(object):
def __init__(self, type_id, name, icon, mime_types):
self.type_id = type_id
self.name = name
self.icon = icon
self.mime_types = []
def to_dict(self):
return { 'type_id' : self.type_id,
'name' : self.name,
'icon' : self.icon
}
class ObjectTypeRegistry(object):
def __init__(self):
bus = dbus.SessionBus()
bus_object = bus.get_object(_SERVICE, _PATH)
self._registry = dbus.Interface(bus_object, _IFACE)
def get_type(type_id):
type_dict = self._registry.GetType(type_id)
return _object_type_from_dict(type_dict)
def get_type_for_mime(mime_type):
type_dict = self._registry.GetTypeForMime(type_id)
return _object_type_from_dict(type_dict)
_registry = ObjectRegistry()
def get_registry():
return _registry