Merge branch 'master' of git+ssh://j5@dev.laptop.org/git/sugar
This commit is contained in:
@@ -22,8 +22,25 @@
|
||||
(gtype-id "SUGAR_TYPE_MENU")
|
||||
)
|
||||
|
||||
(define-object IconEntry
|
||||
(in-module "Sexy")
|
||||
(parent "GtkEntry")
|
||||
(c-name "SexyIconEntry")
|
||||
(gtype-id "SEXY_TYPE_ICON_ENTRY")
|
||||
)
|
||||
|
||||
;; Enumerations and flags ...
|
||||
|
||||
(define-enum IconEntryPosition
|
||||
(in-module "Sexy")
|
||||
(c-name "SexyIconEntryPosition")
|
||||
(gtype-id "SEXY_TYPE_ICON_ENTRY_POSITION")
|
||||
(values
|
||||
'("primary" "SEXY_ICON_ENTRY_PRIMARY")
|
||||
'("secondary" "SEXY_ICON_ENTRY_SECONDARY")
|
||||
)
|
||||
)
|
||||
|
||||
;; From sugar-menu.h
|
||||
|
||||
(define-method set_active
|
||||
@@ -94,3 +111,61 @@
|
||||
'("const-char*" "property")
|
||||
)
|
||||
)
|
||||
|
||||
;; From sexy-icon-entry.h
|
||||
|
||||
(define-function sexy_icon_entry_get_type
|
||||
(c-name "sexy_icon_entry_get_type")
|
||||
(return-type "GType")
|
||||
)
|
||||
|
||||
(define-function sexy_icon_entry_new
|
||||
(c-name "sexy_icon_entry_new")
|
||||
(is-constructor-of "SexyIconEntry")
|
||||
(return-type "GtkWidget*")
|
||||
)
|
||||
|
||||
(define-method set_icon
|
||||
(of-object "SexyIconEntry")
|
||||
(c-name "sexy_icon_entry_set_icon")
|
||||
(return-type "none")
|
||||
(parameters
|
||||
'("SexyIconEntryPosition" "position")
|
||||
'("GtkImage*" "icon")
|
||||
)
|
||||
)
|
||||
|
||||
(define-method set_icon_highlight
|
||||
(of-object "SexyIconEntry")
|
||||
(c-name "sexy_icon_entry_set_icon_highlight")
|
||||
(return-type "none")
|
||||
(parameters
|
||||
'("SexyIconEntryPosition" "position")
|
||||
'("gboolean" "highlight")
|
||||
)
|
||||
)
|
||||
|
||||
(define-method get_icon
|
||||
(of-object "SexyIconEntry")
|
||||
(c-name "sexy_icon_entry_get_icon")
|
||||
(return-type "GtkImage*")
|
||||
(parameters
|
||||
'("SexyIconEntryPosition" "position")
|
||||
)
|
||||
)
|
||||
|
||||
(define-method get_icon_highlight
|
||||
(of-object "SexyIconEntry")
|
||||
(c-name "sexy_icon_entry_get_icon_highlight")
|
||||
(return-type "gboolean")
|
||||
(parameters
|
||||
'("SexyIconEntryPosition" "position")
|
||||
)
|
||||
)
|
||||
|
||||
(define-method add_clear_button
|
||||
(of-object "SexyIconEntry")
|
||||
(c-name "sexy_icon_entry_add_clear_button")
|
||||
(return-type "none")
|
||||
)
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ headers
|
||||
#include "sugar-key-grabber.h"
|
||||
#include "sugar-menu.h"
|
||||
#include "sugar-x11-util.h"
|
||||
#include "sexy-icon-entry.h"
|
||||
|
||||
#include <pygtk/pygtk.h>
|
||||
#include <glib.h>
|
||||
@@ -20,6 +21,7 @@ import gtk.Entry as PyGtkEntry_Type
|
||||
import gtk.Menu as PyGtkMenu_Type
|
||||
import gtk.Container as PyGtkContainer_Type
|
||||
import gtk.gdk.Window as PyGdkWindow_Type
|
||||
import gtk.Image as PyGtkImage_Type
|
||||
%%
|
||||
ignore-glob
|
||||
*_get_type
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
extern PyMethodDef py_sugaruiext_functions[];
|
||||
|
||||
void py_sugaruiext_register_classes (PyObject *d);
|
||||
void py_sugaruiext_add_constants (PyObject *module, const gchar *strip_prefix);
|
||||
|
||||
DL_EXPORT(void)
|
||||
init_sugaruiext(void)
|
||||
@@ -39,6 +40,7 @@ init_sugaruiext(void)
|
||||
d = PyModule_GetDict (m);
|
||||
|
||||
py_sugaruiext_register_classes (d);
|
||||
py_sugaruiext_add_constants(m, "SEXY_");
|
||||
|
||||
if (PyErr_Occurred ()) {
|
||||
Py_FatalError ("can't initialise module _sugaruiext");
|
||||
|
||||
@@ -52,6 +52,8 @@ class ActivityToolbar(gtk.Toolbar):
|
||||
self._activity = activity
|
||||
activity.connect('shared', self._activity_shared_cb)
|
||||
activity.connect('joined', self._activity_shared_cb)
|
||||
activity.connect('notify::max_participants',
|
||||
self._max_participants_changed_cb)
|
||||
|
||||
if activity.metadata:
|
||||
self.title = gtk.Entry()
|
||||
@@ -74,11 +76,11 @@ class ActivityToolbar(gtk.Toolbar):
|
||||
'theme:zoom-home-mini')
|
||||
self.share.combo.append_item(None, _('My Neighborhood'),
|
||||
'theme:zoom-neighborhood-mini')
|
||||
self._update_share()
|
||||
|
||||
self.insert(self.share, -1)
|
||||
self.share.show()
|
||||
|
||||
self._update_share()
|
||||
|
||||
self.keep = ToolButton('document-save')
|
||||
self.keep.set_tooltip(_('Keep'))
|
||||
self.keep.connect('clicked', self._keep_clicked_cb)
|
||||
@@ -94,6 +96,9 @@ class ActivityToolbar(gtk.Toolbar):
|
||||
self._update_title_sid = None
|
||||
|
||||
def _update_share(self):
|
||||
if self._activity.props.max_participants == 1:
|
||||
self.share.hide()
|
||||
|
||||
if self._activity.get_shared():
|
||||
self.share.set_sensitive(False)
|
||||
self.share.combo.set_active(self.SHARE_NEIGHBORHOOD)
|
||||
@@ -139,6 +144,9 @@ class ActivityToolbar(gtk.Toolbar):
|
||||
def _activity_shared_cb(self, activity):
|
||||
self._update_share()
|
||||
|
||||
def _max_participants_changed_cb(self, activity, pspec):
|
||||
self._update_share()
|
||||
|
||||
class EditToolbar(gtk.Toolbar):
|
||||
def __init__(self):
|
||||
gtk.Toolbar.__init__(self)
|
||||
@@ -185,7 +193,10 @@ class Activity(Window, gtk.Container):
|
||||
}
|
||||
|
||||
__gproperties__ = {
|
||||
'active': (bool, None, None, False, gobject.PARAM_READWRITE)
|
||||
'active' : (bool, None, None, False,
|
||||
gobject.PARAM_READWRITE),
|
||||
'max-participants': (int, None, None, 0, 1000, 0,
|
||||
gobject.PARAM_READWRITE)
|
||||
}
|
||||
|
||||
def __init__(self, handle, create_jobject=True):
|
||||
@@ -235,6 +246,7 @@ class Activity(Window, gtk.Container):
|
||||
self._preview = None
|
||||
self._updating_jobject = False
|
||||
self._closing = False
|
||||
self._max_participants = 0
|
||||
|
||||
shared_activity = handle.get_shared_activity()
|
||||
if shared_activity:
|
||||
@@ -280,10 +292,14 @@ class Activity(Window, gtk.Container):
|
||||
self._active = value
|
||||
if not self._active and self._jobject:
|
||||
self.save()
|
||||
elif pspec.name == 'max-participants':
|
||||
self._max_participants = value
|
||||
|
||||
def do_get_property(self, pspec):
|
||||
if pspec.name == 'active':
|
||||
return self._active
|
||||
elif pspec.name == 'max-participants':
|
||||
return self._max_participants
|
||||
|
||||
def get_id(self):
|
||||
return self._activity_id
|
||||
|
||||
@@ -56,6 +56,6 @@ class ActivityService(dbus.service.Object):
|
||||
self._activity = activity
|
||||
|
||||
@dbus.service.method(_ACTIVITY_INTERFACE)
|
||||
def set_active(self, active):
|
||||
def SetActive(self, active):
|
||||
logging.debug('ActivityService.set_active: %s.' % active)
|
||||
self._activity.props.active = active
|
||||
|
||||
@@ -21,6 +21,7 @@ import zipfile
|
||||
import shutil
|
||||
import subprocess
|
||||
import re
|
||||
import gettext
|
||||
|
||||
from sugar import env
|
||||
from sugar.activity.bundle import Bundle
|
||||
@@ -117,7 +118,7 @@ setup.py dist - create a bundle package \n\
|
||||
setup.py install [dirname] - install the bundle \n\
|
||||
setup.py uninstall [dirname] - uninstall the bundle \n\
|
||||
setup.py genpot - generate the gettext pot file \n\
|
||||
setup.py genmo - compile gettext po files in mo \n\
|
||||
setup.py genl10n - generate localization files \n\
|
||||
setup.py clean - clean the directory \n\
|
||||
setup.py release - do a new release of the bundle \n\
|
||||
setup.py help - print this message \n\
|
||||
@@ -157,17 +158,26 @@ def _get_po_list(manifest):
|
||||
|
||||
return file_list
|
||||
|
||||
def _get_mo_list(manifest):
|
||||
mo_list = []
|
||||
def _get_l10n_list(manifest):
|
||||
l10n_list = []
|
||||
|
||||
for lang in _get_po_list(manifest).keys():
|
||||
filename = _get_service_name() + '.mo'
|
||||
mo_list.append(os.path.join('locale', lang, 'LC_MESSAGES', filename))
|
||||
l10n_list.append(os.path.join('locale', lang, 'LC_MESSAGES', filename))
|
||||
l10n_list.append(os.path.join('locale', lang, 'activity.linfo'))
|
||||
|
||||
return mo_list
|
||||
return l10n_list
|
||||
|
||||
def _get_activity_name():
|
||||
info_path = os.path.join(_get_source_path(), 'activity', 'activity.info')
|
||||
f = open(info_path,'r')
|
||||
info = f.read()
|
||||
f.close()
|
||||
match = re.search('^name\s*=\s*(.*)$', info, flags = re.MULTILINE)
|
||||
return match.group(1)
|
||||
|
||||
def cmd_dist(bundle_name, manifest):
|
||||
cmd_genmo(bundle_name, manifest)
|
||||
cmd_genl10n(bundle_name, manifest)
|
||||
file_list = _get_file_list(manifest)
|
||||
|
||||
zipname = _get_package_name(bundle_name)
|
||||
@@ -177,7 +187,7 @@ def cmd_dist(bundle_name, manifest):
|
||||
for filename in file_list:
|
||||
bundle_zip.write(filename, os.path.join(base_dir, filename))
|
||||
|
||||
for filename in _get_mo_list(manifest):
|
||||
for filename in _get_l10n_list(manifest):
|
||||
bundle_zip.write(filename, os.path.join(base_dir, filename))
|
||||
|
||||
bundle_zip.close()
|
||||
@@ -205,8 +215,21 @@ def cmd_genpot(bundle_name, manifest):
|
||||
if file_name.endswith('.py'):
|
||||
python_files.append(file_name)
|
||||
|
||||
# First write out a stub .pot file containing just the translated
|
||||
# activity name, then have xgettext merge the rest of the
|
||||
# translations into that. (We can't just append the activity name
|
||||
# to the end of the .pot file afterwards, because that might
|
||||
# create a duplicate msgid.)
|
||||
pot_file = os.path.join('po', '%s.pot' % bundle_name)
|
||||
args = [ 'xgettext', '--language=Python',
|
||||
activity_name = _get_activity_name()
|
||||
escaped_name = re.sub('([\\\\"])', '\\\\\\1', activity_name)
|
||||
f = open(pot_file, 'w')
|
||||
f.write('#: activity/activity.info:2\n')
|
||||
f.write('msgid "%s"\n' % escaped_name)
|
||||
f.write('msgstr ""\n')
|
||||
f.close()
|
||||
|
||||
args = [ 'xgettext', '--join-existing', '--language=Python',
|
||||
'--keyword=_', '--output=%s' % pot_file ]
|
||||
|
||||
args += python_files
|
||||
@@ -220,14 +243,16 @@ def cmd_genpot(bundle_name, manifest):
|
||||
if retcode:
|
||||
print 'ERROR - msgmerge failed with return code %i.' % retcode
|
||||
|
||||
def cmd_genmo(bundle_name, manifest):
|
||||
def cmd_genl10n(bundle_name, manifest):
|
||||
source_path = _get_source_path()
|
||||
activity_name = _get_activity_name()
|
||||
|
||||
po_list = _get_po_list(manifest)
|
||||
for lang in po_list.keys():
|
||||
file_name = po_list[lang]
|
||||
|
||||
mo_path = os.path.join(source_path, 'locale', lang, 'LC_MESSAGES')
|
||||
localedir = os.path.join(source_path, 'locale', lang)
|
||||
mo_path = os.path.join(localedir, 'LC_MESSAGES')
|
||||
if not os.path.isdir(mo_path):
|
||||
os.makedirs(mo_path)
|
||||
|
||||
@@ -237,6 +262,13 @@ def cmd_genmo(bundle_name, manifest):
|
||||
if retcode:
|
||||
print 'ERROR - msgfmt failed with return code %i.' % retcode
|
||||
|
||||
cat = gettext.GNUTranslations(open(mo_file, 'r'))
|
||||
translated_name = cat.gettext(activity_name)
|
||||
linfo_file = os.path.join(localedir, 'activity.linfo')
|
||||
f = open(linfo_file, 'w')
|
||||
f.write('[Activity]\nname = %s\n' % translated_name)
|
||||
f.close()
|
||||
|
||||
def cmd_release(bundle_name, manifest):
|
||||
if not os.path.isdir('.git'):
|
||||
print 'ERROR - this command works only for git repositories'
|
||||
@@ -358,8 +390,8 @@ def start(bundle_name, manifest='MANIFEST'):
|
||||
cmd_uninstall(sys.argv[2])
|
||||
elif sys.argv[1] == 'genpot':
|
||||
cmd_genpot(bundle_name, manifest)
|
||||
elif sys.argv[1] == 'genmo':
|
||||
cmd_genmo(bundle_name, manifest)
|
||||
elif sys.argv[1] == 'genl10n':
|
||||
cmd_genl10n(bundle_name, manifest)
|
||||
elif sys.argv[1] == 'clean':
|
||||
cmd_clean()
|
||||
elif sys.argv[1] == 'release':
|
||||
|
||||
@@ -23,7 +23,7 @@ NAME_KEY = 'NAME'
|
||||
PERCENT_KEY = 'PERCENT'
|
||||
ICON_KEY = 'ICON'
|
||||
PREVIEW_KEY = 'PREVIEW'
|
||||
ACTIVITY_KEY = 'ACTIVITY'
|
||||
ACTIVITIES_KEY = 'ACTIVITIES'
|
||||
FORMATS_KEY = 'FORMATS'
|
||||
|
||||
TYPE_KEY = 'TYPE'
|
||||
@@ -51,7 +51,7 @@ class ClipboardService(gobject.GObject):
|
||||
'object-deleted': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
|
||||
([str])),
|
||||
'object-state-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
|
||||
([str, str, int, str, str, str])),
|
||||
([str, str, int, str, str, object])),
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
@@ -118,13 +118,13 @@ class ClipboardService(gobject.GObject):
|
||||
percent
|
||||
icon
|
||||
preview
|
||||
activity
|
||||
activities
|
||||
|
||||
From the ClipboardObject instance which is being described.
|
||||
"""
|
||||
self.emit('object-state-changed', str(object_id), values[NAME_KEY],
|
||||
values[PERCENT_KEY], values[ICON_KEY], values[PREVIEW_KEY],
|
||||
values[ACTIVITY_KEY])
|
||||
values[ACTIVITIES_KEY])
|
||||
|
||||
def add_object(self, name):
|
||||
"""Add a new object to the path
|
||||
@@ -193,7 +193,7 @@ class ClipboardService(gobject.GObject):
|
||||
PERCENT_KEY: number,
|
||||
ICON_KEY: str,
|
||||
PREVIEW_KEY: XXX what is it?,
|
||||
ACTIVITY_KEY: source activity id,
|
||||
ACTIVITIES_KEY: activities that can open this object,
|
||||
FORMATS_KEY: list of XXX what is it?
|
||||
"""
|
||||
return self._dbus_service.get_object(dbus.ObjectPath(object_id),)
|
||||
|
||||
@@ -120,6 +120,9 @@ class DSObject(object):
|
||||
|
||||
def resume(self, service_name=None):
|
||||
if self.is_bundle():
|
||||
if service_name is not None:
|
||||
raise ValueError('Object is a bundle, cannot be resumed as an activity.')
|
||||
|
||||
bundle = Bundle(self.file_path)
|
||||
if not bundle.is_installed():
|
||||
bundle.install()
|
||||
|
||||
@@ -28,55 +28,63 @@ DS_DBUS_SERVICE = "org.laptop.sugar.DataStore"
|
||||
DS_DBUS_INTERFACE = "org.laptop.sugar.DataStore"
|
||||
DS_DBUS_PATH = "/org/laptop/sugar/DataStore"
|
||||
|
||||
_bus = dbus.SessionBus()
|
||||
_data_store = dbus.Interface(_bus.get_object(DS_DBUS_SERVICE, DS_DBUS_PATH),
|
||||
DS_DBUS_INTERFACE)
|
||||
_data_store = None
|
||||
|
||||
def _get_data_store():
|
||||
global _data_store
|
||||
|
||||
if not _data_store:
|
||||
_bus = dbus.SessionBus()
|
||||
_data_store = dbus.Interface(_bus.get_object(DS_DBUS_SERVICE,
|
||||
DS_DBUS_PATH),
|
||||
DS_DBUS_INTERFACE)
|
||||
return _data_store
|
||||
|
||||
def create(properties, filename):
|
||||
object_id = _data_store.create(dbus.Dictionary(properties), filename)
|
||||
object_id = _get_data_store().create(dbus.Dictionary(properties), filename)
|
||||
logging.debug('dbus_helpers.create: ' + object_id)
|
||||
return object_id
|
||||
|
||||
def update(uid, properties, filename, reply_handler=None, error_handler=None, timeout=-1):
|
||||
logging.debug('dbus_helpers.update: %s, %s, %s' % (uid, filename, properties))
|
||||
if reply_handler and error_handler:
|
||||
_data_store.update(uid, dbus.Dictionary(properties), filename,
|
||||
_get_data_store().update(uid, dbus.Dictionary(properties), filename,
|
||||
reply_handler=reply_handler,
|
||||
error_handler=error_handler,
|
||||
timeout=timeout)
|
||||
else:
|
||||
_data_store.update(uid, dbus.Dictionary(properties), filename)
|
||||
_get_data_store().update(uid, dbus.Dictionary(properties), filename)
|
||||
|
||||
def delete(uid):
|
||||
logging.debug('dbus_helpers.delete: %r' % uid)
|
||||
_data_store.delete(uid)
|
||||
_get_data_store().delete(uid)
|
||||
|
||||
def get_properties(uid):
|
||||
logging.debug('dbus_helpers.get_properties: %s' % uid)
|
||||
return _data_store.get_properties(uid)
|
||||
return _get_data_store().get_properties(uid)
|
||||
|
||||
def get_filename(uid):
|
||||
filename = _data_store.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, reply_handler, error_handler):
|
||||
logging.debug('dbus_helpers.find: %r' % query)
|
||||
if reply_handler and error_handler:
|
||||
return _data_store.find(query, reply_handler=reply_handler,
|
||||
return _get_data_store().find(query, reply_handler=reply_handler,
|
||||
error_handler=error_handler)
|
||||
else:
|
||||
return _data_store.find(query)
|
||||
return _get_data_store().find(query)
|
||||
|
||||
def mount(uri, options):
|
||||
return _data_store.mount(uri, options)
|
||||
return _get_data_store().mount(uri, options)
|
||||
|
||||
def unmount(mount_point_id):
|
||||
_data_store.unmount(mount_point_id)
|
||||
_get_data_store().unmount(mount_point_id)
|
||||
|
||||
def mounts():
|
||||
return _data_store.mounts()
|
||||
return _get_data_store().mounts()
|
||||
|
||||
def get_unique_values(key):
|
||||
return _data_store.get_uniquevaluesfor(key, dbus.Dictionary({}, signature='ss'))
|
||||
return _get_data_store().get_uniquevaluesfor(key, dbus.Dictionary({}, signature='ss'))
|
||||
|
||||
|
||||
+7
-7
@@ -21,12 +21,13 @@ import datetime
|
||||
|
||||
class Date(object):
|
||||
"""Date-object storing a simple time.time() float
|
||||
|
||||
XXX not sure about the rationale for this class,
|
||||
possibly it makes transfer over dbus easier?
|
||||
|
||||
Useful to display dates in the UI in an
|
||||
abbreviated and easy to read format.
|
||||
"""
|
||||
def __init__(self, timestamp):
|
||||
"""Initialise via a timestamp (floating point value)"""
|
||||
self._today = datetime.date.today()
|
||||
self._timestamp = timestamp
|
||||
|
||||
def __str__(self):
|
||||
@@ -39,14 +40,13 @@ class Date(object):
|
||||
the year in the date.
|
||||
"""
|
||||
date = datetime.date.fromtimestamp(self._timestamp)
|
||||
today = datetime.date.today()
|
||||
|
||||
# FIXME localization
|
||||
if date == today:
|
||||
if date == self._today:
|
||||
result = 'Today'
|
||||
elif date == today - datetime.timedelta(1):
|
||||
elif date == self._today - datetime.timedelta(1):
|
||||
result = 'Yesterday'
|
||||
elif date.year == today.year:
|
||||
elif date.year == self._today.year:
|
||||
result = date.strftime('%B %d')
|
||||
else:
|
||||
result = date.strftime('%B %d, %Y')
|
||||
|
||||
@@ -9,7 +9,7 @@ sugar_PYTHON = \
|
||||
combobox.py \
|
||||
icon.py \
|
||||
iconbutton.py \
|
||||
menuitem.py \
|
||||
iconentry.py \
|
||||
notebook.py \
|
||||
objectchooser.py \
|
||||
radiotoolbutton.py \
|
||||
|
||||
@@ -25,7 +25,7 @@ class CanvasButton(hippo.CanvasButton):
|
||||
hippo.CanvasButton.__init__(self, text=label)
|
||||
|
||||
if icon_name:
|
||||
icon = Icon(icon_name, gtk.ICON_SIZE_BUTTON)
|
||||
icon = Icon(icon_name,icon_size=gtk.ICON_SIZE_BUTTON)
|
||||
self.props.widget.set_image(icon)
|
||||
icon.show()
|
||||
|
||||
|
||||
@@ -142,6 +142,8 @@ class CanvasIcon(hippo.CanvasBox, hippo.CanvasItem):
|
||||
gobject.PARAM_READWRITE),
|
||||
'size' : (int, None, None, 0, 1024, 0,
|
||||
gobject.PARAM_READWRITE),
|
||||
'scale' : (int, None, None, 0, 1024, 0,
|
||||
gobject.PARAM_READWRITE),
|
||||
'cache' : (bool, None, None, False,
|
||||
gobject.PARAM_READWRITE),
|
||||
'active' : (bool, None, None, True,
|
||||
@@ -156,6 +158,7 @@ class CanvasIcon(hippo.CanvasBox, hippo.CanvasItem):
|
||||
self._buffers = {}
|
||||
self._cur_buffer = None
|
||||
self._size = 0
|
||||
self._scale = 0
|
||||
self._fill_color = None
|
||||
self._stroke_color = None
|
||||
self._icon_name = None
|
||||
@@ -210,6 +213,11 @@ class CanvasIcon(hippo.CanvasBox, hippo.CanvasItem):
|
||||
self._clear_buffers()
|
||||
self._size = value
|
||||
self.emit_request_changed()
|
||||
elif pspec.name == 'scale':
|
||||
if self._scale != value and not self._cache:
|
||||
self._clear_buffers()
|
||||
self._scale = value
|
||||
self.emit_request_changed()
|
||||
elif pspec.name == 'cache':
|
||||
self._cache = value
|
||||
elif pspec.name == 'active':
|
||||
@@ -277,6 +285,8 @@ class CanvasIcon(hippo.CanvasBox, hippo.CanvasItem):
|
||||
return self._active
|
||||
elif pspec.name == 'badge-name':
|
||||
return self._badge_name
|
||||
elif pspec.name == 'scale':
|
||||
return self._scale
|
||||
|
||||
def _get_icon_size(self, handle):
|
||||
if handle:
|
||||
@@ -286,9 +296,11 @@ class CanvasIcon(hippo.CanvasBox, hippo.CanvasItem):
|
||||
return [0, 0]
|
||||
|
||||
def _get_size(self, handle):
|
||||
if self._size == 0:
|
||||
width, height = self._get_icon_size(handle)
|
||||
else:
|
||||
width, height = self._get_icon_size(handle)
|
||||
if self._scale != 0:
|
||||
width = int(width * self._scale)
|
||||
height = int(height * self._scale)
|
||||
elif self._size != 0:
|
||||
width = height = self._size
|
||||
|
||||
return [width, height]
|
||||
|
||||
+19
-8
@@ -34,17 +34,18 @@ class Icon(gtk.Image):
|
||||
gobject.PARAM_READWRITE)
|
||||
}
|
||||
|
||||
def __init__(self, name, size=gtk.ICON_SIZE_LARGE_TOOLBAR, **kwargs):
|
||||
def __init__(self, name, **kwargs):
|
||||
self._constructed = False
|
||||
self._fill_color = None
|
||||
self._stroke_color = None
|
||||
self._icon_name = name
|
||||
self._size = size
|
||||
self._theme = gtk.icon_theme_get_default()
|
||||
self._data = None
|
||||
|
||||
gobject.GObject.__init__(self, **kwargs)
|
||||
|
||||
# If we have a non-styled-icon
|
||||
if not self._fill_color and not self._stroke_color:
|
||||
self._update_normal_icon()
|
||||
self._constructed = True
|
||||
self._update_icon()
|
||||
|
||||
def _get_pixbuf(self, data, width, height):
|
||||
loader = gtk.gdk.PixbufLoader('svg')
|
||||
@@ -77,9 +78,12 @@ class Icon(gtk.Image):
|
||||
source.set_state(gtk.STATE_INSENSITIVE)
|
||||
icon_set.add_source(source)
|
||||
|
||||
self.set_from_icon_set(icon_set, self._size)
|
||||
self.props.icon_set = icon_set
|
||||
|
||||
def _update_icon(self):
|
||||
if not self._constructed:
|
||||
return
|
||||
|
||||
if not self._fill_color and not self._stroke_color:
|
||||
self._update_normal_icon()
|
||||
return
|
||||
@@ -100,12 +104,12 @@ class Icon(gtk.Image):
|
||||
self._data = data
|
||||
|
||||
# Redraw pixbuf
|
||||
[w, h] = gtk.icon_size_lookup(self._size)
|
||||
[w, h] = gtk.icon_size_lookup(self.props.icon_size)
|
||||
pixbuf = self._get_pixbuf(self._data, w, h)
|
||||
self.set_from_pixbuf(pixbuf)
|
||||
|
||||
def _get_real_name(self, name):
|
||||
info = self._theme.lookup_icon(name, self._size, 0)
|
||||
info = self._theme.lookup_icon(name, self.props.icon_size, 0)
|
||||
if not info:
|
||||
raise ValueError("Icon '" + name + "' not found.")
|
||||
fname = info.get_filename()
|
||||
@@ -122,9 +126,16 @@ class Icon(gtk.Image):
|
||||
elif pspec.name == 'stroke-color':
|
||||
self._stroke_color = value
|
||||
self._update_icon()
|
||||
else:
|
||||
gtk.Image.do_set_property(self, pspec, value)
|
||||
|
||||
if pspec.name == 'icon-size':
|
||||
self._update_icon()
|
||||
|
||||
def do_get_property(self, pspec):
|
||||
if pspec.name == 'fill-color':
|
||||
return self._fill_color
|
||||
elif pspec.name == 'stroke-color':
|
||||
return self._stroke_color
|
||||
else:
|
||||
return gtk.Image.do_get_property(self, pspec)
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
# Copyright (C) 2007, One Laptop Per Child
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library 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
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the
|
||||
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
# Boston, MA 02111-1307, USA.
|
||||
|
||||
import gtk
|
||||
|
||||
from sugar import _sugaruiext
|
||||
|
||||
ICON_ENTRY_PRIMARY = _sugaruiext.ICON_ENTRY_PRIMARY
|
||||
ICON_ENTRY_SECONDARY = _sugaruiext.ICON_ENTRY_SECONDARY
|
||||
|
||||
class IconEntry(_sugaruiext.IconEntry):
|
||||
def set_icon_from_name(self, position, name):
|
||||
icon_theme = gtk.icon_theme_get_default()
|
||||
icon_info = icon_theme.lookup_icon(name,
|
||||
gtk.ICON_SIZE_SMALL_TOOLBAR,
|
||||
0)
|
||||
|
||||
pixbuf = gtk.gdk.pixbuf_new_from_file(icon_info.get_filename())
|
||||
|
||||
image = gtk.Image()
|
||||
image.set_from_pixbuf(pixbuf)
|
||||
image.show()
|
||||
|
||||
self.set_icon(position, image)
|
||||
|
||||
def set_icon(self, position, image):
|
||||
if image.get_storage_type() not in [gtk.IMAGE_PIXBUF, gtk.IMAGE_STOCK]:
|
||||
raise ValueError('Image must have a storage type of pixbuf or ' +
|
||||
'stock, not %r.' % image.get_storage_type())
|
||||
_sugaruiext.IconEntry.set_icon(self, position, image)
|
||||
|
||||
@@ -22,7 +22,7 @@ class MenuItem(gtk.ImageMenuItem):
|
||||
def __init__(self, text_label, icon_name=None):
|
||||
gtk.ImageMenuItem.__init__(self, text_label)
|
||||
if icon_name:
|
||||
icon = Icon(icon_name, gtk.ICON_SIZE_MENU)
|
||||
icon = Icon(icon_name, icon_size=gtk.ICON_SIZE_MENU)
|
||||
self.set_image(icon)
|
||||
icon.show()
|
||||
|
||||
|
||||
@@ -150,7 +150,7 @@ class CollapsedEntry(CanvasRoundBox):
|
||||
self._icon_name = type.icon
|
||||
|
||||
if not self._icon_name:
|
||||
self._icon_name = 'theme:stock-missing'
|
||||
self._icon_name = 'theme:image-missing'
|
||||
|
||||
return self._icon_name
|
||||
|
||||
|
||||
+60
-34
@@ -87,9 +87,7 @@ class Palette(gtk.Window):
|
||||
'invoker' : (object, None, None,
|
||||
gobject.PARAM_READWRITE),
|
||||
'position' : (gobject.TYPE_INT, None, None, 0, 6,
|
||||
0, gobject.PARAM_READWRITE),
|
||||
'draw-gap' : (bool, None, None, False,
|
||||
gobject.PARAM_READWRITE)
|
||||
0, gobject.PARAM_READWRITE)
|
||||
}
|
||||
|
||||
__gsignals__ = {
|
||||
@@ -114,7 +112,6 @@ class Palette(gtk.Window):
|
||||
self._group_id = None
|
||||
self._up = False
|
||||
self._position = self.DEFAULT
|
||||
self._draw_gap = False
|
||||
self._palette_popup_sid = None
|
||||
|
||||
self._popup_anim = animator.Animator(0.3, 10)
|
||||
@@ -162,6 +159,7 @@ class Palette(gtk.Window):
|
||||
self._leave_notify_event_cb)
|
||||
|
||||
self.set_primary_text(label, accel_path)
|
||||
self.set_group_id('default')
|
||||
|
||||
def is_up(self):
|
||||
return self._up
|
||||
@@ -178,8 +176,9 @@ class Palette(gtk.Window):
|
||||
return gtk.gdk.Rectangle(x, y, width, height)
|
||||
|
||||
def set_primary_text(self, label, accel_path=None):
|
||||
self._label.set_text(label)
|
||||
self._label.show()
|
||||
if label is not None:
|
||||
self._label.set_text(label)
|
||||
self._label.show()
|
||||
|
||||
def set_content(self, widget):
|
||||
if len(self._content.get_children()) > 0:
|
||||
@@ -196,6 +195,7 @@ class Palette(gtk.Window):
|
||||
group = palettegroup.get_group(self._group_id)
|
||||
group.remove(self)
|
||||
if group_id:
|
||||
self._group_id = group_id
|
||||
group = palettegroup.get_group(group_id)
|
||||
group.add(self)
|
||||
|
||||
@@ -206,9 +206,6 @@ class Palette(gtk.Window):
|
||||
self._invoker.connect('mouse-leave', self._invoker_mouse_leave_cb)
|
||||
elif pspec.name == 'position':
|
||||
self._position = value
|
||||
elif pspec.name == 'draw-gap':
|
||||
self._draw_gap = value
|
||||
self.queue_draw()
|
||||
else:
|
||||
raise AssertionError
|
||||
|
||||
@@ -217,8 +214,6 @@ class Palette(gtk.Window):
|
||||
return self._invoker
|
||||
elif pspec.name == 'position':
|
||||
return self._position
|
||||
elif pspec.name == 'draw-gap':
|
||||
return self._draw_gap
|
||||
else:
|
||||
raise AssertionError
|
||||
|
||||
@@ -228,7 +223,7 @@ class Palette(gtk.Window):
|
||||
|
||||
def do_expose_event(self, event):
|
||||
# We want to draw a border with a beautiful gap
|
||||
if self._draw_gap:
|
||||
if self._invoker.has_rectangle_gap():
|
||||
invoker = self._invoker.get_rect()
|
||||
palette = self.get_rect()
|
||||
|
||||
@@ -398,6 +393,9 @@ class Palette(gtk.Window):
|
||||
self.menu.set_active(True)
|
||||
self.show()
|
||||
|
||||
if self._invoker:
|
||||
self._invoker.notify_popup()
|
||||
|
||||
self._up = True
|
||||
_palette_observer.emit('popup', self)
|
||||
self.emit('popup')
|
||||
@@ -406,22 +404,31 @@ class Palette(gtk.Window):
|
||||
if not self._palette_popup_sid is None:
|
||||
_palette_observer.disconnect(self._palette_popup_sid)
|
||||
self._palette_popup_sid = None
|
||||
|
||||
self.menu.set_active(False)
|
||||
self.hide()
|
||||
|
||||
if self._invoker:
|
||||
self._invoker.notify_popdown()
|
||||
|
||||
self._up = False
|
||||
self.emit('popdown')
|
||||
|
||||
def popup(self):
|
||||
def popup(self, immediate=False):
|
||||
self._popdown_anim.stop()
|
||||
self._popup_anim.start()
|
||||
|
||||
if not immediate:
|
||||
self._popup_anim.start()
|
||||
else:
|
||||
self._show()
|
||||
|
||||
self._secondary_anim.start()
|
||||
|
||||
def popdown(self, inmediate=False):
|
||||
def popdown(self, immediate=False):
|
||||
self._secondary_anim.stop()
|
||||
self._popup_anim.stop()
|
||||
|
||||
if not inmediate:
|
||||
if not immediate:
|
||||
self._popdown_anim.start()
|
||||
else:
|
||||
self._hide()
|
||||
@@ -440,7 +447,15 @@ class Palette(gtk.Window):
|
||||
self._state = state
|
||||
|
||||
def _invoker_mouse_enter_cb(self, invoker):
|
||||
self.popup()
|
||||
immediate = False
|
||||
if self._group_id:
|
||||
group = palettegroup.get_group(self._group_id)
|
||||
if group and group.is_up():
|
||||
immediate = True
|
||||
group.popdown()
|
||||
|
||||
print immediate
|
||||
self.popup(immediate=immediate)
|
||||
|
||||
def _invoker_mouse_leave_cb(self, invoker):
|
||||
self.popdown()
|
||||
@@ -535,6 +550,12 @@ class Invoker(gobject.GObject):
|
||||
def __init__(self):
|
||||
gobject.GObject.__init__(self)
|
||||
|
||||
def has_rectangle_gap(self):
|
||||
return False
|
||||
|
||||
def draw_rectangle(self, event, palette):
|
||||
pass
|
||||
|
||||
def get_default_position(self):
|
||||
return Palette.AROUND
|
||||
|
||||
@@ -543,6 +564,12 @@ class Invoker(gobject.GObject):
|
||||
height = gtk.gdk.screen_height()
|
||||
return gtk.gdk.Rectangle(0, 0, width, height)
|
||||
|
||||
def notify_popup(self):
|
||||
pass
|
||||
|
||||
def notify_popdown(self):
|
||||
pass
|
||||
|
||||
class WidgetInvoker(Invoker):
|
||||
def __init__(self, widget):
|
||||
Invoker.__init__(self)
|
||||
@@ -562,31 +589,24 @@ class WidgetInvoker(Invoker):
|
||||
|
||||
return gtk.gdk.Rectangle(x, y, width, height)
|
||||
|
||||
def draw_invoker_rect(self, event, palette):
|
||||
style = self._widget.style
|
||||
if palette.is_up():
|
||||
gap = _calculate_gap(self.get_rect(), palette.get_rect())
|
||||
def has_rectangle_gap(self):
|
||||
return True
|
||||
|
||||
if gap:
|
||||
style.paint_box_gap(event.window, gtk.STATE_PRELIGHT,
|
||||
gtk.SHADOW_IN, event.area, self._widget,
|
||||
"palette-invoker",
|
||||
self._widget.allocation.x,
|
||||
self._widget.allocation.y,
|
||||
self._widget.allocation.width,
|
||||
self._widget.allocation.height,
|
||||
gap[0], gap[1], gap[2])
|
||||
else:
|
||||
style.paint_box(event.window, gtk.STATE_PRELIGHT,
|
||||
def draw_rectangle(self, event, palette):
|
||||
style = self._widget.style
|
||||
gap = _calculate_gap(self.get_rect(), palette.get_rect())
|
||||
if gap:
|
||||
style.paint_box_gap(event.window, gtk.STATE_PRELIGHT,
|
||||
gtk.SHADOW_IN, event.area, self._widget,
|
||||
"palette-invoker",
|
||||
self._widget.allocation.x,
|
||||
self._widget.allocation.y,
|
||||
self._widget.allocation.width,
|
||||
self._widget.allocation.height)
|
||||
self._widget.allocation.height,
|
||||
gap[0], gap[1], gap[2])
|
||||
else:
|
||||
style.paint_box(event.window, gtk.STATE_PRELIGHT,
|
||||
gtk.SHADOW_NONE, event.area, self._widget,
|
||||
gtk.SHADOW_IN, event.area, self._widget,
|
||||
"palette-invoker",
|
||||
self._widget.allocation.x,
|
||||
self._widget.allocation.y,
|
||||
@@ -602,6 +622,12 @@ class WidgetInvoker(Invoker):
|
||||
def get_toplevel(self):
|
||||
return self._widget.get_toplevel()
|
||||
|
||||
def notify_popup(self):
|
||||
self._widget.queue_draw()
|
||||
|
||||
def notify_popdown(self):
|
||||
self._widget.queue_draw()
|
||||
|
||||
class CanvasInvoker(Invoker):
|
||||
def __init__(self, item):
|
||||
Invoker.__init__(self)
|
||||
|
||||
@@ -39,6 +39,7 @@ class Group(gobject.GObject):
|
||||
gobject.GObject.__init__(self)
|
||||
self._up = False
|
||||
self._palettes = []
|
||||
self._sig_ids = {}
|
||||
|
||||
def is_up(self):
|
||||
return self._up
|
||||
@@ -46,18 +47,26 @@ class Group(gobject.GObject):
|
||||
def add(self, palette):
|
||||
self._palettes.append(palette)
|
||||
|
||||
self._sig_ids[palette] = []
|
||||
|
||||
sid = palette.connect('popup', self._palette_popup_cb)
|
||||
palette.popup_sid = sid
|
||||
self._sig_ids[palette].append(sid)
|
||||
|
||||
sid = palette.connect('popdown', self._palette_popdown_cb)
|
||||
palette.podown_sid = sid
|
||||
self._sig_ids[palette].append(sid)
|
||||
|
||||
def remove(self, palette):
|
||||
self.disconnect(palette.popup_sid)
|
||||
self.disconnect(palette.popdown_sid)
|
||||
sig_ids = self._sig_ids[palette]
|
||||
for sid in sig_ids:
|
||||
palette.disconnect(sid)
|
||||
|
||||
self._palettes.remove(palette)
|
||||
|
||||
def popdown(self):
|
||||
for palette in self._palettes:
|
||||
if palette.is_up():
|
||||
palette.popdown(immediate=True)
|
||||
|
||||
def _palette_popup_cb(self, palette):
|
||||
if not self._up:
|
||||
self.emit('popup')
|
||||
|
||||
@@ -40,25 +40,15 @@ class RadioToolButton(gtk.RadioToolButton):
|
||||
def set_palette(self, palette):
|
||||
self._palette = palette
|
||||
self._palette.props.invoker = WidgetInvoker(self.child)
|
||||
self._palette.props.draw_gap = True
|
||||
|
||||
self._palette.connect("popup", self._palette_changed)
|
||||
self._palette.connect("popdown", self._palette_changed)
|
||||
|
||||
def set_tooltip(self, text):
|
||||
self._palette = Palette(text)
|
||||
self._palette.props.invoker = WidgetInvoker(self.child)
|
||||
self._set_palette(Palette(text))
|
||||
|
||||
def do_expose_event(self, event):
|
||||
if self._palette and self._palette.props.draw_gap:
|
||||
if self._palette.is_up() or self.child.state == gtk.STATE_PRELIGHT:
|
||||
invoker = self._palette.props.invoker
|
||||
invoker.draw_invoker_rect(event, self._palette)
|
||||
if self._palette and self._palette.is_up():
|
||||
invoker = self._palette.props.invoker
|
||||
invoker.draw_rectangle(event, self._palette)
|
||||
|
||||
gtk.RadioToolButton.do_expose_event(self, event)
|
||||
|
||||
def _palette_changed(self, palette):
|
||||
# Force a redraw to update the invoker rectangle
|
||||
self.queue_draw()
|
||||
|
||||
palette = property(get_palette, set_palette)
|
||||
|
||||
@@ -128,6 +128,8 @@ COLOR_WHITE = Color('#FFFFFF')
|
||||
COLOR_TRANSPARENT = Color('#FFFFFF', alpha=0.0)
|
||||
COLOR_PANEL_GREY = Color('#C0C0C0')
|
||||
COLOR_SELECTION_GREY = Color('#A6A6A6')
|
||||
COLOR_TOOLBAR_GREY = Color('#404040')
|
||||
COLOR_BUTTON_GREY = Color('#808080')
|
||||
COLOR_INACTIVE_FILL = Color('#9D9FA1')
|
||||
COLOR_INACTIVE_STROKE = Color('#757575')
|
||||
|
||||
|
||||
@@ -39,25 +39,15 @@ class ToggleToolButton(gtk.ToggleToolButton):
|
||||
def set_palette(self, palette):
|
||||
self._palette = palette
|
||||
self._palette.props.invoker = WidgetInvoker(self.child)
|
||||
self._palette.props.draw_gap = True
|
||||
|
||||
self._palette.connect("popup", self._palette_changed)
|
||||
self._palette.connect("popdown", self._palette_changed)
|
||||
|
||||
def set_tooltip(self, text):
|
||||
self._palette = Palette(text)
|
||||
self._palette.props.invoker = WidgetInvoker(self.child)
|
||||
self._set_palette(Palette(text))
|
||||
|
||||
def do_expose_event(self, event):
|
||||
if self._palette and self._palette.props.draw_gap:
|
||||
if self._palette.is_up() or self.child.state == gtk.STATE_PRELIGHT:
|
||||
invoker = self._palette.props.invoker
|
||||
invoker.draw_invoker_rect(event, self._palette)
|
||||
if self._palette and self._palette.is_up():
|
||||
invoker = self._palette.props.invoker
|
||||
invoker.draw_rectangle(event, self._palette)
|
||||
|
||||
gtk.ToggleToolButton.do_expose_event(self, event)
|
||||
|
||||
def _palette_changed(self, palette):
|
||||
# Force a redraw to update the invoker rectangle
|
||||
self.queue_draw()
|
||||
|
||||
palette = property(get_palette, set_palette)
|
||||
|
||||
@@ -28,7 +28,8 @@ class ToolButton(gtk.ToolButton):
|
||||
def __init__(self, icon_name=None):
|
||||
gtk.ToolButton.__init__(self)
|
||||
self._palette = None
|
||||
self.set_icon(icon_name)
|
||||
if icon_name:
|
||||
self.set_icon(icon_name)
|
||||
self.connect('clicked', self._button_clicked_cb)
|
||||
|
||||
def set_icon(self, icon_name):
|
||||
@@ -42,19 +43,14 @@ class ToolButton(gtk.ToolButton):
|
||||
def set_palette(self, palette):
|
||||
self._palette = palette
|
||||
self._palette.props.invoker = WidgetInvoker(self.child)
|
||||
self._palette.props.draw_gap = True
|
||||
|
||||
self._palette.connect("popup", self._palette_changed)
|
||||
self._palette.connect("popdown", self._palette_changed)
|
||||
|
||||
def set_tooltip(self, text):
|
||||
self.set_palette(Palette(text))
|
||||
|
||||
def do_expose_event(self, event):
|
||||
if self._palette and self._palette.props.draw_gap:
|
||||
if self._palette.is_up() or self.child.state == gtk.STATE_PRELIGHT:
|
||||
invoker = self._palette.props.invoker
|
||||
invoker.draw_invoker_rect(event, self._palette)
|
||||
if self._palette and self._palette.is_up():
|
||||
invoker = self._palette.props.invoker
|
||||
invoker.draw_rectangle(event, self._palette)
|
||||
|
||||
gtk.ToolButton.do_expose_event(self, event)
|
||||
|
||||
@@ -62,8 +58,4 @@ class ToolButton(gtk.ToolButton):
|
||||
if self._palette:
|
||||
self._palette.popdown(True)
|
||||
|
||||
def _palette_changed(self, palette):
|
||||
# Force a redraw to update the invoker rectangle
|
||||
self.queue_draw()
|
||||
|
||||
palette = property(get_palette, set_palette)
|
||||
|
||||
+1
-5
@@ -105,11 +105,7 @@ def _get_logs_dir():
|
||||
def start(module_id):
|
||||
# Only log if logging is set up for the activity
|
||||
module_key = module_id.upper() + "_DEBUG"
|
||||
emulator = False
|
||||
if os.environ.has_key("SUGAR_EMULATOR"):
|
||||
if os.environ["SUGAR_EMULATOR"] == "yes":
|
||||
emulator = True
|
||||
if not os.environ.has_key(module_key) and not emulator:
|
||||
if not os.environ.has_key(module_key) and not env.is_emulator():
|
||||
return
|
||||
|
||||
log_writer = LogWriter(module_id)
|
||||
|
||||
+32
-6
@@ -1,4 +1,5 @@
|
||||
# Copyright (C) 2006-2007, Red Hat, Inc.
|
||||
# Copyright (C) 2007, One Laptop Per Child
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
@@ -20,7 +21,13 @@ import logging
|
||||
from sugar import _sugarext
|
||||
|
||||
def get_for_file(file_name):
|
||||
return _sugarext.get_mime_type_for_file(file_name)
|
||||
mime_type = _sugarext.get_mime_type_for_file(file_name)
|
||||
if mime_type == 'application/octet-stream':
|
||||
if _file_looks_like_text(file_name):
|
||||
return 'text/plain'
|
||||
else:
|
||||
return 'application/octet-stream'
|
||||
return mime_type
|
||||
|
||||
def get_from_file_name(file_name):
|
||||
return _sugarext.get_mime_type_from_file_name(file_name)
|
||||
@@ -51,13 +58,9 @@ def choose_most_significant(mime_types):
|
||||
if 'text/uri-list' in mime_types:
|
||||
return 'text/uri-list'
|
||||
|
||||
for mime_category in ['image/', 'text/', 'application/']:
|
||||
for mime_category in ['image/', 'application/']:
|
||||
for mime_type in mime_types:
|
||||
|
||||
# skip text/plain and text/html, these have lower priority.
|
||||
if mime_type in ['text/plain', 'text/html']:
|
||||
continue
|
||||
|
||||
if mime_type.startswith(mime_category):
|
||||
# skip mozilla private types (second component starts with '_'
|
||||
# or ends with '-priv')
|
||||
@@ -70,6 +73,10 @@ def choose_most_significant(mime_types):
|
||||
logging.debug('Choosed %r!' % mime_type)
|
||||
return mime_type
|
||||
|
||||
if 'text/x-moz-url' in mime_types:
|
||||
logging.debug('Choosed text/x-moz-url!')
|
||||
return 'text/x-moz-url'
|
||||
|
||||
if 'text/html' in mime_types:
|
||||
logging.debug('Choosed text/html!')
|
||||
return 'text/html'
|
||||
@@ -80,3 +87,22 @@ def choose_most_significant(mime_types):
|
||||
|
||||
logging.debug('Returning first: %r.' % mime_types[0])
|
||||
return mime_types[0]
|
||||
|
||||
def _file_looks_like_text(file_name):
|
||||
f = open(file_name, 'r')
|
||||
try:
|
||||
sample = f.read(256)
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
if '\000' in sample:
|
||||
return False
|
||||
|
||||
for encoding in ('ascii', 'latin_1', 'utf_8', 'utf_16'):
|
||||
try:
|
||||
string = unicode(sample, encoding)
|
||||
return True
|
||||
except Exception, e:
|
||||
pass
|
||||
|
||||
return False
|
||||
|
||||
@@ -3,5 +3,6 @@ sugar_PYTHON = \
|
||||
__init__.py \
|
||||
activity.py \
|
||||
buddy.py \
|
||||
tubeconn.py \
|
||||
presenceservice.py
|
||||
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
# This should eventually land in telepathy-python, so has the same license:
|
||||
|
||||
# Copyright (C) 2007 Collabora Ltd. <http://www.collabora.co.uk/>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published
|
||||
# by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
|
||||
__all__ = ('TubeConnection',)
|
||||
__docformat__ = 'reStructuredText'
|
||||
|
||||
|
||||
import logging
|
||||
|
||||
from dbus.connection import Connection
|
||||
|
||||
|
||||
logger = logging.getLogger('telepathy.tubeconn')
|
||||
|
||||
|
||||
class TubeConnection(Connection):
|
||||
|
||||
def __new__(cls, conn, tubes_iface, tube_id, address=None,
|
||||
group_iface=None, mainloop=None):
|
||||
if address is None:
|
||||
address = tubes_iface.GetDBusServerAddress(tube_id)
|
||||
self = super(TubeConnection, cls).__new__(cls, address,
|
||||
mainloop=mainloop)
|
||||
|
||||
self._tubes_iface = tubes_iface
|
||||
self.tube_id = tube_id
|
||||
self.participants = {}
|
||||
self.bus_name_to_handle = {}
|
||||
self._mapping_watches = []
|
||||
|
||||
if group_iface is None:
|
||||
method = conn.GetSelfHandle
|
||||
else:
|
||||
method = group_iface.GetSelfHandle
|
||||
method(reply_handler=self._on_get_self_handle_reply,
|
||||
error_handler=self._on_get_self_handle_error)
|
||||
|
||||
return self
|
||||
|
||||
def _on_get_self_handle_reply(self, handle):
|
||||
self.self_handle = handle
|
||||
match = self._tubes_iface.connect_to_signal('DBusNamesChanged',
|
||||
self._on_dbus_names_changed)
|
||||
self._tubes_iface.GetDBusNames(self.tube_id,
|
||||
reply_handler=self._on_get_dbus_names_reply,
|
||||
error_handler=self._on_get_dbus_names_error)
|
||||
self._dbus_names_changed_match = match
|
||||
|
||||
def _on_get_self_handle_error(self, e):
|
||||
logging.basicConfig()
|
||||
logger.error('GetSelfHandle failed: %s', e)
|
||||
|
||||
def close(self):
|
||||
self._dbus_names_changed_match.remove()
|
||||
self._on_dbus_names_changed(self.tube_id, (), self.participants.keys())
|
||||
super(TubeConnection, self).close()
|
||||
|
||||
def _on_get_dbus_names_reply(self, names):
|
||||
self._on_dbus_names_changed(self.tube_id, names, ())
|
||||
|
||||
def _on_get_dbus_names_error(self, e):
|
||||
logging.basicConfig()
|
||||
logger.error('GetDBusNames failed: %s', e)
|
||||
|
||||
def _on_dbus_names_changed(self, tube_id, added, removed):
|
||||
if tube_id == self.tube_id:
|
||||
for handle, bus_name in added:
|
||||
if handle == self.self_handle:
|
||||
# I've just joined - set my unique name
|
||||
self.set_unique_name(bus_name)
|
||||
self.participants[handle] = bus_name
|
||||
self.bus_name_to_handle[bus_name] = handle
|
||||
|
||||
# call the callback while the removed people are still in
|
||||
# participants, so their bus names are available
|
||||
for callback in self._mapping_watches:
|
||||
callback(added, removed)
|
||||
|
||||
for handle in removed:
|
||||
bus_name = self.participants.pop(handle, None)
|
||||
self.bus_name_to_handle.pop(bus_name, None)
|
||||
|
||||
def watch_participants(self, callback):
|
||||
self._mapping_watches.append(callback)
|
||||
if self.participants:
|
||||
# GetDBusNames already returned: fake a participant add event
|
||||
# immediately
|
||||
added = []
|
||||
for k, v in self.participants.iteritems():
|
||||
added.append((k, v))
|
||||
callback(added, [])
|
||||
Reference in New Issue
Block a user