Cleanup the source structure
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
sugardir = $(pythondir)/sugar/graphics
|
||||
sugar_PYTHON = \
|
||||
__init__.py \
|
||||
alert.py \
|
||||
animator.py \
|
||||
combobox.py \
|
||||
entry.py \
|
||||
icon.py \
|
||||
iconentry.py \
|
||||
menuitem.py \
|
||||
notebook.py \
|
||||
objectchooser.py \
|
||||
radiotoolbutton.py \
|
||||
palette.py \
|
||||
palettegroup.py \
|
||||
panel.py \
|
||||
roundbox.py \
|
||||
spreadlayout.py \
|
||||
style.py \
|
||||
toggletoolbutton.py \
|
||||
toolbox.py \
|
||||
toolbutton.py \
|
||||
toolcombobox.py \
|
||||
tray.py \
|
||||
window.py \
|
||||
xocolor.py
|
||||
@@ -0,0 +1,18 @@
|
||||
"""Graphics/controls for use in Sugar"""
|
||||
|
||||
# Copyright (C) 2006-2007, Red Hat, Inc.
|
||||
#
|
||||
# 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.
|
||||
@@ -0,0 +1,230 @@
|
||||
# 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.
|
||||
|
||||
from gettext import gettext as _
|
||||
|
||||
import gtk
|
||||
import gobject
|
||||
import hippo
|
||||
import math
|
||||
|
||||
from sugar.graphics import style
|
||||
from sugar.graphics.icon import Icon
|
||||
|
||||
|
||||
class Alert(gtk.EventBox, gobject.GObject):
|
||||
"""UI interface for Alerts
|
||||
|
||||
Alerts are used inside the activity window instead of being a
|
||||
separate popup window. They do not hide canvas content. You can
|
||||
use add_alert(widget) and remove_alert(widget) inside your activity
|
||||
to add and remove the alert. You can set the position (bottom=-1,
|
||||
top=0,1) for alerts global for the window by changing alert_position,
|
||||
default is bottom.
|
||||
|
||||
Properties:
|
||||
'title': the title of the alert,
|
||||
'message': the message of the alert,
|
||||
'icon': the icon that appears at the far left
|
||||
See __gproperties__
|
||||
"""
|
||||
|
||||
__gtype_name__ = 'SugarAlert'
|
||||
|
||||
__gsignals__ = {
|
||||
'response': (gobject.SIGNAL_RUN_FIRST,
|
||||
gobject.TYPE_NONE, ([object]))
|
||||
}
|
||||
|
||||
__gproperties__ = {
|
||||
'title' : (str, None, None, None,
|
||||
gobject.PARAM_READWRITE),
|
||||
'msg' : (str, None, None, None,
|
||||
gobject.PARAM_READWRITE),
|
||||
'icon' : (object, None, None,
|
||||
gobject.PARAM_WRITABLE)
|
||||
}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
gobject.GObject.__init__(self)
|
||||
|
||||
self.set_visible_window(True)
|
||||
self._hbox = gtk.HBox()
|
||||
self.add(self._hbox)
|
||||
|
||||
self._title = None
|
||||
self._msg = None
|
||||
self._icon = None
|
||||
self._timeout = 0
|
||||
self._buttons = {}
|
||||
|
||||
self._msg_box = gtk.VBox()
|
||||
self._title_label = gtk.Label()
|
||||
size = style.zoom(style.GRID_CELL_SIZE * 0.5)
|
||||
self._title_label.set_alignment(0, 0.5)
|
||||
self._title_label.set_padding(style.DEFAULT_SPACING, 0)
|
||||
self._msg_box.pack_start(self._title_label, False)
|
||||
self._title_label.show()
|
||||
|
||||
self._msg_label = gtk.Label()
|
||||
self._msg_label.set_alignment(0, 0.5)
|
||||
self._msg_label.set_padding(style.DEFAULT_SPACING, 0)
|
||||
self._msg_box.pack_start(self._msg_label, False)
|
||||
self._msg_label.show()
|
||||
self._hbox.pack_start(self._msg_box)
|
||||
|
||||
self._buttons_box = gtk.HButtonBox()
|
||||
self._buttons_box.set_layout(gtk.BUTTONBOX_END)
|
||||
self._buttons_box.set_spacing(style.DEFAULT_SPACING)
|
||||
self._hbox.pack_start(self._buttons_box)
|
||||
self._buttons_box.show()
|
||||
|
||||
self._msg_box.show()
|
||||
|
||||
self._hbox.show()
|
||||
self.show()
|
||||
|
||||
def do_set_property(self, pspec, value):
|
||||
if pspec.name == 'title':
|
||||
if self._title != value:
|
||||
self._title = value
|
||||
self._title_label.set_markup("<b>" + self._title + "</b>")
|
||||
elif pspec.name == 'msg':
|
||||
if self._msg != value:
|
||||
self._msg = value
|
||||
self._msg_label.set_markup(self._msg)
|
||||
elif pspec.name == 'icon':
|
||||
if self._icon != value:
|
||||
self._icon = value
|
||||
self._hbox.pack_start(self._icon)
|
||||
self._hbox.reorder_child(self._icon, 0)
|
||||
|
||||
def do_get_property(self, pspec):
|
||||
if pspec.name == 'title':
|
||||
return self._title
|
||||
elif pspec.name == 'msg':
|
||||
return self._msg
|
||||
|
||||
def add_button(self, response_id, label, icon=None, position=-1):
|
||||
"""Add a button to the alert
|
||||
|
||||
response_id: will be emitted with the response signal
|
||||
a response ID should one of the pre-defined
|
||||
GTK Response Type Constants or a positive number
|
||||
label: that will occure right to the buttom
|
||||
icon: this can be a SugarIcon or a gtk.Image
|
||||
position: the position of the button in the box (optional)
|
||||
"""
|
||||
button = gtk.Button()
|
||||
self._buttons[response_id] = button
|
||||
if icon is not None:
|
||||
button.set_image(icon)
|
||||
button.set_label(label)
|
||||
self._buttons_box.pack_start(button)
|
||||
button.show()
|
||||
button.connect('clicked', self.__button_clicked_cb, response_id)
|
||||
if position != -1:
|
||||
self._buttons_box.reorder_child(button, position)
|
||||
return button
|
||||
|
||||
def remove_button(self, response_id):
|
||||
"""Remove a button from the alert by the given button id"""
|
||||
self._buttons_box.remove(self._buttons[id])
|
||||
|
||||
def _response(self, id):
|
||||
"""Emitting response when we have a result
|
||||
|
||||
A result can be that a user has clicked a button or
|
||||
a timeout has occured, the id identifies the button
|
||||
that has been clicked and -1 for a timeout
|
||||
"""
|
||||
self.emit('response', id)
|
||||
|
||||
def __button_clicked_cb(self, button, response_id):
|
||||
self._response(response_id)
|
||||
|
||||
|
||||
class ConfirmationAlert(Alert):
|
||||
"""This is a ready-made two button (Cancel,Ok) alert"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
Alert.__init__(self, **kwargs)
|
||||
|
||||
icon = Icon(icon_name='dialog-cancel')
|
||||
cancel_button = self.add_button(gtk.RESPONSE_CANCEL, _('Cancel'), icon)
|
||||
icon.show()
|
||||
|
||||
icon = Icon(icon_name='dialog-ok')
|
||||
ok_button = self.add_button(gtk.RESPONSE_OK, _('Ok'), icon)
|
||||
icon.show()
|
||||
|
||||
|
||||
class _TimeoutIcon(hippo.CanvasText, hippo.CanvasItem):
|
||||
__gtype_name__ = 'AlertTimeoutIcon'
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
hippo.CanvasText.__init__(self, **kwargs)
|
||||
|
||||
self.props.orientation = hippo.ORIENTATION_HORIZONTAL
|
||||
self.props.border_left = style.DEFAULT_SPACING
|
||||
self.props.border_right = style.DEFAULT_SPACING
|
||||
|
||||
def do_paint_background(self, cr, damaged_box):
|
||||
[width, height] = self.get_allocation()
|
||||
|
||||
x = width * 0.5
|
||||
y = height * 0.5
|
||||
radius = min(width * 0.5, height * 0.5)
|
||||
|
||||
hippo.cairo_set_source_rgba32(cr, self.props.background_color)
|
||||
cr.arc(x, y, radius, 0, 2*math.pi)
|
||||
cr.fill_preserve()
|
||||
|
||||
|
||||
class TimeoutAlert(Alert):
|
||||
"""This is a ready-made two button (Cancel,Continue) alert
|
||||
|
||||
It times out with a positive reponse after the given amount of seconds.
|
||||
"""
|
||||
|
||||
def __init__(self, timeout=5, **kwargs):
|
||||
Alert.__init__(self, **kwargs)
|
||||
|
||||
self._timeout = timeout
|
||||
|
||||
icon = Icon(icon_name='dialog-cancel')
|
||||
cancel_button = self.add_button(gtk.RESPONSE_CANCEL, _('Cancel'), icon)
|
||||
icon.show()
|
||||
|
||||
self._timeout_text = _TimeoutIcon(
|
||||
text=self._timeout,
|
||||
color=style.COLOR_BUTTON_GREY.get_int(),
|
||||
background_color=style.COLOR_WHITE.get_int())
|
||||
canvas = hippo.Canvas()
|
||||
canvas.set_root(self._timeout_text)
|
||||
canvas.show()
|
||||
self.add_button(gtk.RESPONSE_OK, _('Continue'), canvas)
|
||||
|
||||
gobject.timeout_add(1000, self.__timeout)
|
||||
|
||||
def __timeout(self):
|
||||
self._timeout -= 1
|
||||
self._timeout_text.props.text = self._timeout
|
||||
if self._timeout == 0:
|
||||
self._response(gtk.RESPONSE_OK)
|
||||
return False
|
||||
return True
|
||||
@@ -0,0 +1,94 @@
|
||||
# Copyright (C) 2007, Red Hat, Inc.
|
||||
#
|
||||
# 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 time
|
||||
|
||||
import gobject
|
||||
|
||||
EASE_OUT_EXPO = 0
|
||||
EASE_IN_EXPO = 1
|
||||
|
||||
class Animator(gobject.GObject):
|
||||
__gsignals__ = {
|
||||
'completed': (gobject.SIGNAL_RUN_FIRST,
|
||||
gobject.TYPE_NONE, ([])),
|
||||
}
|
||||
|
||||
def __init__(self, time, fps=20, easing=EASE_OUT_EXPO):
|
||||
gobject.GObject.__init__(self)
|
||||
self._animations = []
|
||||
self._time = time
|
||||
self._interval = 1.0 / fps
|
||||
self._easing = easing
|
||||
self._timeout_sid = 0
|
||||
|
||||
def add(self, animation):
|
||||
self._animations.append(animation)
|
||||
|
||||
def remove_all(self):
|
||||
self.stop()
|
||||
self._animations = []
|
||||
|
||||
def start(self):
|
||||
if self._timeout_sid:
|
||||
self.stop()
|
||||
|
||||
self._start_time = time.time()
|
||||
self._timeout_sid = gobject.timeout_add(
|
||||
int(self._interval * 1000), self._next_frame_cb)
|
||||
|
||||
def stop(self):
|
||||
if self._timeout_sid:
|
||||
gobject.source_remove(self._timeout_sid)
|
||||
self._timeout_sid = 0
|
||||
self.emit('completed')
|
||||
|
||||
def _next_frame_cb(self):
|
||||
current_time = min(self._time, time.time() - self._start_time)
|
||||
current_time = max(current_time, 0.0)
|
||||
|
||||
for animation in self._animations:
|
||||
animation.do_frame(current_time, self._time, self._easing)
|
||||
|
||||
if current_time == self._time:
|
||||
self.stop()
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
class Animation(object):
|
||||
def __init__(self, start, end):
|
||||
self.start = start
|
||||
self.end = end
|
||||
|
||||
def do_frame(self, time, duration, easing):
|
||||
start = self.start
|
||||
change = self.end - self.start
|
||||
|
||||
if time == duration:
|
||||
# last frame
|
||||
frame = self.end
|
||||
else:
|
||||
if easing == EASE_OUT_EXPO:
|
||||
frame = change * (-pow(2, -10 * time/duration) + 1) + start;
|
||||
elif easing == EASE_IN_EXPO:
|
||||
frame = change * pow(2, 10 * (time / duration - 1)) + start;
|
||||
|
||||
self.next_frame(frame)
|
||||
|
||||
def next_frame(self, frame):
|
||||
pass
|
||||
@@ -0,0 +1,114 @@
|
||||
# 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 sys
|
||||
import os
|
||||
import logging
|
||||
|
||||
import gobject
|
||||
import gtk
|
||||
|
||||
class ComboBox(gtk.ComboBox):
|
||||
__gtype_name__ = 'SugarComboBox'
|
||||
|
||||
__gproperties__ = {
|
||||
'value' : (object, None, None,
|
||||
gobject.PARAM_READABLE)
|
||||
}
|
||||
def __init__(self):
|
||||
gtk.ComboBox.__init__(self)
|
||||
|
||||
self._text_renderer = None
|
||||
self._icon_renderer = None
|
||||
|
||||
self._model = gtk.ListStore(gobject.TYPE_PYOBJECT,
|
||||
gobject.TYPE_STRING,
|
||||
gtk.gdk.Pixbuf,
|
||||
gobject.TYPE_BOOLEAN)
|
||||
self.set_model(self._model)
|
||||
|
||||
self.set_row_separator_func(self._is_separator)
|
||||
|
||||
def do_get_property(self, pspec):
|
||||
if pspec.name == 'value':
|
||||
row = self.get_active_item()
|
||||
if not row:
|
||||
return None
|
||||
return row[0]
|
||||
else:
|
||||
return gtk.ComboBox.do_get_property(self, pspec)
|
||||
|
||||
def _get_real_name_from_theme(self, name, size):
|
||||
icon_theme = gtk.icon_theme_get_default()
|
||||
width, height = gtk.icon_size_lookup(size)
|
||||
info = icon_theme.lookup_icon(name, width, 0)
|
||||
if not info:
|
||||
raise ValueError("Icon '" + name + "' not found.")
|
||||
fname = info.get_filename()
|
||||
del info
|
||||
return fname
|
||||
|
||||
def append_item(self, action_id, text, icon_name=None, file_name=None):
|
||||
if not self._icon_renderer and (icon_name or file_name):
|
||||
self._icon_renderer = gtk.CellRendererPixbuf()
|
||||
|
||||
settings = self.get_settings()
|
||||
w, h = gtk.icon_size_lookup_for_settings(settings, gtk.ICON_SIZE_MENU)
|
||||
self._icon_renderer.props.stock_size = w
|
||||
|
||||
self.pack_start(self._icon_renderer, False)
|
||||
self.add_attribute(self._icon_renderer, 'pixbuf', 2)
|
||||
|
||||
if not self._text_renderer and text:
|
||||
self._text_renderer = gtk.CellRendererText()
|
||||
self.pack_end(self._text_renderer, True)
|
||||
self.add_attribute(self._text_renderer, 'text', 1)
|
||||
|
||||
if icon_name or file_name:
|
||||
if text:
|
||||
size = gtk.ICON_SIZE_MENU
|
||||
else:
|
||||
size = gtk.ICON_SIZE_LARGE_TOOLBAR
|
||||
width, height = gtk.icon_size_lookup(size)
|
||||
|
||||
if icon_name:
|
||||
file_name = self._get_real_name_from_theme(icon_name, size)
|
||||
|
||||
pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(file_name, width, height)
|
||||
else:
|
||||
pixbuf = None
|
||||
|
||||
self._model.append([action_id, text, pixbuf, False])
|
||||
|
||||
def append_separator(self):
|
||||
self._model.append([0, None, None, True])
|
||||
|
||||
def get_active_item(self):
|
||||
index = self.get_active()
|
||||
if index == -1:
|
||||
index = 0
|
||||
|
||||
row = self._model.iter_nth_child(None, index)
|
||||
if not row:
|
||||
return None
|
||||
return self._model[row]
|
||||
|
||||
def remove_all(self):
|
||||
self._model.clear()
|
||||
|
||||
def _is_separator(self, model, row):
|
||||
action_id, text, icon_name, is_separator = model[row]
|
||||
return is_separator
|
||||
@@ -0,0 +1,25 @@
|
||||
# Copyright (C) 2007, Red Hat, Inc.
|
||||
#
|
||||
# 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
|
||||
import hippo
|
||||
|
||||
class CanvasEntry(hippo.CanvasEntry):
|
||||
def set_background(self, color_spec):
|
||||
color = gtk.gdk.color_parse(color_spec)
|
||||
self.props.widget.modify_bg(gtk.STATE_INSENSITIVE, color)
|
||||
self.props.widget.modify_base(gtk.STATE_INSENSITIVE, color)
|
||||
@@ -0,0 +1,504 @@
|
||||
# Copyright (C) 2006-2007 Red Hat, Inc.
|
||||
#
|
||||
# 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 os
|
||||
import re
|
||||
import math
|
||||
import time
|
||||
import logging
|
||||
|
||||
import gobject
|
||||
import gtk
|
||||
import hippo
|
||||
import rsvg
|
||||
import cairo
|
||||
|
||||
from sugar.graphics.style import Color
|
||||
from sugar.graphics.xocolor import XoColor
|
||||
from sugar.graphics import style
|
||||
from sugar.graphics.palette import Palette, CanvasInvoker
|
||||
from sugar.util import LRU
|
||||
|
||||
_BADGE_SIZE = 0.45
|
||||
|
||||
class _SVGLoader(object):
|
||||
def __init__(self):
|
||||
self._cache = LRU(50)
|
||||
|
||||
def load(self, file_name, entities, cache):
|
||||
if file_name in self._cache:
|
||||
icon = self._cache[file_name]
|
||||
else:
|
||||
icon_file = open(file_name, 'r')
|
||||
icon = icon_file.read()
|
||||
icon_file.close()
|
||||
|
||||
if cache:
|
||||
self._cache[file_name] = icon
|
||||
|
||||
for entity, value in entities.items():
|
||||
if isinstance(value, basestring):
|
||||
xml = '<!ENTITY %s "%s">' % (entity, value)
|
||||
icon = re.sub('<!ENTITY %s .*>' % entity, xml, icon)
|
||||
else:
|
||||
logging.error(
|
||||
'Icon %s, entity %s is invalid.', file_name, entity)
|
||||
|
||||
return rsvg.Handle(data=icon)
|
||||
|
||||
class _IconInfo(object):
|
||||
def __init__(self):
|
||||
self.file_name = None
|
||||
self.attach_x = 0
|
||||
self.attach_y = 0
|
||||
|
||||
class _BadgeInfo(object):
|
||||
def __init__(self):
|
||||
self.attach_x = 0
|
||||
self.attach_y = 0
|
||||
self.size = 0
|
||||
self.icon_padding = 0
|
||||
|
||||
class _IconBuffer(object):
|
||||
_surface_cache = LRU(50)
|
||||
_loader = _SVGLoader()
|
||||
|
||||
def __init__(self):
|
||||
self.icon_name = None
|
||||
self.file_name = None
|
||||
self.fill_color = None
|
||||
self.stroke_color = None
|
||||
self.badge_name = None
|
||||
self.width = None
|
||||
self.height = None
|
||||
self.cache = False
|
||||
|
||||
def _get_cache_key(self):
|
||||
return (self.icon_name, self.file_name, self.fill_color,
|
||||
self.stroke_color, self.badge_name, self.width, self.height)
|
||||
|
||||
def _load_svg(self, file_name):
|
||||
entities = {}
|
||||
if self.fill_color:
|
||||
entities['fill_color'] = self.fill_color
|
||||
if self.stroke_color:
|
||||
entities['stroke_color'] = self.stroke_color
|
||||
|
||||
return self._loader.load(file_name, entities, self.cache)
|
||||
|
||||
def _get_attach_points(self, info, size_request):
|
||||
attach_points = info.get_attach_points()
|
||||
|
||||
if attach_points:
|
||||
attach_x = float(attach_points[0][0]) / size_request
|
||||
attach_y = float(attach_points[0][1]) / size_request
|
||||
else:
|
||||
attach_x = attach_y = 0
|
||||
|
||||
return attach_x, attach_y
|
||||
|
||||
def _get_icon_info(self):
|
||||
icon_info = _IconInfo()
|
||||
|
||||
if self.file_name:
|
||||
icon_info.file_name = self.file_name
|
||||
elif self.icon_name:
|
||||
theme = gtk.icon_theme_get_default()
|
||||
|
||||
size = 50
|
||||
if self.width != None:
|
||||
size = self.width
|
||||
|
||||
info = theme.lookup_icon(self.icon_name, size, 0)
|
||||
if info:
|
||||
attach_x, attach_y = self._get_attach_points(info, size)
|
||||
|
||||
icon_info.file_name = info.get_filename()
|
||||
icon_info.attach_x = attach_x
|
||||
icon_info.attach_y = attach_y
|
||||
else:
|
||||
logging.warning('No icon with the name %s '
|
||||
'was found in the theme.' % self.icon_name)
|
||||
|
||||
return icon_info
|
||||
|
||||
def _draw_badge(self, context, size):
|
||||
theme = gtk.icon_theme_get_default()
|
||||
badge_info = theme.lookup_icon(self.badge_name, size, 0)
|
||||
if badge_info:
|
||||
badge_file_name = badge_info.get_filename()
|
||||
if badge_file_name.endswith('.svg'):
|
||||
handle = self._loader.load(badge_file_name, {}, self.cache)
|
||||
handle.render_cairo(context)
|
||||
else:
|
||||
pixbuf = gtk.gdk.pixbuf_new_from_file(badge_file_name)
|
||||
surface = hippo.cairo_surface_from_gdk_pixbuf(pixbuf)
|
||||
context.set_source_surface(surface, 0, 0)
|
||||
context.paint()
|
||||
|
||||
def _get_size(self, icon_width, icon_height, padding):
|
||||
if self.width is not None and self.height is not None:
|
||||
width = self.width + padding
|
||||
height = self.height + padding
|
||||
else:
|
||||
width = icon_width + padding
|
||||
height = icon_height + padding
|
||||
|
||||
return width, height
|
||||
|
||||
def _get_badge_info(self, icon_info, icon_width, icon_height):
|
||||
info = _BadgeInfo()
|
||||
if self.badge_name is None:
|
||||
return info
|
||||
|
||||
info.size = int(_BADGE_SIZE * icon_width)
|
||||
info.attach_x = int(icon_info.attach_x * icon_width - info.size / 2)
|
||||
info.attach_y = int(icon_info.attach_y * icon_height - info.size / 2)
|
||||
|
||||
if info.attach_x < 0 or info.attach_y < 0:
|
||||
info.icon_padding = max(-info.attach_x, -info.attach_y)
|
||||
elif info.attach_x + info.size > icon_width or \
|
||||
info.attach_y + info.size > icon_height:
|
||||
x_padding = info.attach_x + info.size - icon_width
|
||||
y_padding = info.attach_y + info.size - icon_height
|
||||
info.icon_padding = max(x_padding, y_padding)
|
||||
|
||||
return info
|
||||
|
||||
def _get_xo_color(self):
|
||||
if self.stroke_color and self.fill_color:
|
||||
return XoColor('%s,%s' % (self.stroke_color, self.fill_color))
|
||||
else:
|
||||
return None
|
||||
|
||||
def _set_xo_color(self, xo_color):
|
||||
if xo_color:
|
||||
self.stroke_color = xo_color.get_stroke_color()
|
||||
self.fill_color = xo_color.get_fill_color()
|
||||
else:
|
||||
self.stroke_color = None
|
||||
self.fill_color = None
|
||||
|
||||
def get_surface(self):
|
||||
cache_key = self._get_cache_key()
|
||||
if cache_key in self._surface_cache:
|
||||
return self._surface_cache[cache_key]
|
||||
|
||||
icon_info = self._get_icon_info()
|
||||
if icon_info.file_name is None:
|
||||
return None
|
||||
|
||||
is_svg = icon_info.file_name.endswith('.svg')
|
||||
|
||||
if is_svg:
|
||||
handle = self._load_svg(icon_info.file_name)
|
||||
dimensions = handle.get_dimension_data()
|
||||
icon_width = int(dimensions[0])
|
||||
icon_height = int(dimensions[1])
|
||||
else:
|
||||
pixbuf = gtk.gdk.pixbuf_new_from_file(icon_info.file_name)
|
||||
icon_width = pixbuf.get_width()
|
||||
icon_height = pixbuf.get_height()
|
||||
|
||||
badge_info = self._get_badge_info(icon_info, icon_width, icon_height)
|
||||
|
||||
padding = badge_info.icon_padding
|
||||
width, height = self._get_size(icon_width, icon_height, padding)
|
||||
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
|
||||
|
||||
context = cairo.Context(surface)
|
||||
context.scale(float(width) / (icon_width + padding * 2),
|
||||
float(height) / (icon_height + padding * 2))
|
||||
context.save()
|
||||
|
||||
context.translate(padding, padding)
|
||||
if is_svg:
|
||||
handle.render_cairo(context)
|
||||
else:
|
||||
pixbuf_surface = hippo.cairo_surface_from_gdk_pixbuf(pixbuf)
|
||||
context.set_source_surface(pixbuf_surface, 0, 0)
|
||||
context.paint()
|
||||
|
||||
if self.badge_name:
|
||||
context.restore()
|
||||
context.translate(badge_info.attach_x, badge_info.attach_y)
|
||||
self._draw_badge(context, badge_info.size)
|
||||
|
||||
self._surface_cache[cache_key] = surface
|
||||
|
||||
return surface
|
||||
|
||||
xo_color = property(_get_xo_color, _set_xo_color)
|
||||
|
||||
class Icon(gtk.Image):
|
||||
__gtype_name__ = 'SugarIcon'
|
||||
|
||||
__gproperties__ = {
|
||||
'xo-color' : (object, None, None,
|
||||
gobject.PARAM_WRITABLE),
|
||||
'fill-color' : (object, None, None,
|
||||
gobject.PARAM_READWRITE),
|
||||
'stroke-color' : (object, None, None,
|
||||
gobject.PARAM_READWRITE),
|
||||
'badge-name' : (str, None, None, None,
|
||||
gobject.PARAM_READWRITE)
|
||||
}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self._buffer = _IconBuffer()
|
||||
|
||||
gobject.GObject.__init__(self, **kwargs)
|
||||
|
||||
def _sync_image_properties(self):
|
||||
if self._buffer.icon_name != self.props.icon_name:
|
||||
self._buffer.icon_name = self.props.icon_name
|
||||
|
||||
if self._buffer.file_name != self.props.file:
|
||||
self._buffer.file_name = self.props.file
|
||||
|
||||
width, height = gtk.icon_size_lookup(self.props.icon_size)
|
||||
if self._buffer.width != width and self._buffer.height != height:
|
||||
self._buffer.width = width
|
||||
self._buffer.height = height
|
||||
|
||||
def _icon_size_changed_cb(self, image, pspec):
|
||||
self._buffer.icon_size = self.props.icon_size
|
||||
|
||||
def _icon_name_changed_cb(self, image, pspec):
|
||||
self._buffer.icon_name = self.props.icon_name
|
||||
|
||||
def _file_changed_cb(self, image, pspec):
|
||||
self._buffer.file_name = self.props.file
|
||||
|
||||
def _update_buffer_size(self):
|
||||
width, height = gtk.icon_size_lookup(self.props.icon_size)
|
||||
|
||||
self._buffer.width = width
|
||||
self._buffer.height = height
|
||||
|
||||
def do_size_request(self, requisition):
|
||||
self._sync_image_properties()
|
||||
surface = self._buffer.get_surface()
|
||||
if surface:
|
||||
requisition[0] = surface.get_width()
|
||||
requisition[1] = surface.get_height()
|
||||
elif self._buffer.width and self._buffer.height:
|
||||
requisition[0] = self._buffer.width
|
||||
requisition[1] = self._buffer.width
|
||||
else:
|
||||
requisition[0] = requisition[1] = 0
|
||||
|
||||
def do_expose_event(self, event):
|
||||
self._sync_image_properties()
|
||||
surface = self._buffer.get_surface()
|
||||
if surface is None:
|
||||
return
|
||||
|
||||
xpad, ypad = self.get_padding()
|
||||
xalign, yalign = self.get_alignment()
|
||||
requisition = self.get_child_requisition()
|
||||
if self.get_direction != gtk.TEXT_DIR_LTR:
|
||||
xalign = 1.0 - xalign
|
||||
|
||||
x = math.floor(self.allocation.x + xpad +
|
||||
(self.allocation.width - requisition[0]) * xalign)
|
||||
y = math.floor(self.allocation.y + ypad +
|
||||
(self.allocation.height - requisition[1]) * yalign)
|
||||
|
||||
cr = self.window.cairo_create()
|
||||
cr.set_source_surface(surface, x, y)
|
||||
cr.paint()
|
||||
|
||||
def do_set_property(self, pspec, value):
|
||||
if pspec.name == 'xo-color':
|
||||
if self._buffer.xo_color != value:
|
||||
self._buffer.xo_color = value
|
||||
self.queue_draw()
|
||||
elif pspec.name == 'fill-color':
|
||||
if self._buffer.fill_color != value:
|
||||
self._buffer.fill_color = value
|
||||
self.queue_draw()
|
||||
elif pspec.name == 'stroke-color':
|
||||
if self._buffer.stroke_color != value:
|
||||
self._buffer.stroke_color = value
|
||||
self.queue_draw()
|
||||
elif pspec.name == 'badge-name':
|
||||
if self._buffer.badge_name != value:
|
||||
self._buffer.badge_name = value
|
||||
self.queue_resize()
|
||||
else:
|
||||
gtk.Image.do_set_property(self, pspec, value)
|
||||
|
||||
def do_get_property(self, pspec):
|
||||
if pspec.name == 'fill-color':
|
||||
return self._buffer.fill_color
|
||||
elif pspec.name == 'stroke-color':
|
||||
return self._buffer.stroke_color
|
||||
elif pspec.name == 'badge-name':
|
||||
return self._buffer.badge_name
|
||||
else:
|
||||
return gtk.Image.do_get_property(self, pspec)
|
||||
|
||||
class CanvasIcon(hippo.CanvasBox, hippo.CanvasItem):
|
||||
__gtype_name__ = 'CanvasIcon'
|
||||
|
||||
__gproperties__ = {
|
||||
'file-name' : (str, None, None, None,
|
||||
gobject.PARAM_READWRITE),
|
||||
'icon-name' : (str, None, None, None,
|
||||
gobject.PARAM_READWRITE),
|
||||
'xo-color' : (object, None, None,
|
||||
gobject.PARAM_WRITABLE),
|
||||
'fill-color' : (object, None, None,
|
||||
gobject.PARAM_READWRITE),
|
||||
'stroke-color' : (object, None, None,
|
||||
gobject.PARAM_READWRITE),
|
||||
'size' : (int, None, None, 0, 1024, 0,
|
||||
gobject.PARAM_READWRITE),
|
||||
'scale' : (float, None, None, -1024.0, 1024.0, 1.0,
|
||||
gobject.PARAM_READWRITE),
|
||||
'cache' : (bool, None, None, False,
|
||||
gobject.PARAM_READWRITE),
|
||||
'badge-name' : (str, None, None, None,
|
||||
gobject.PARAM_READWRITE)
|
||||
}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self._buffer = _IconBuffer()
|
||||
|
||||
hippo.CanvasBox.__init__(self, **kwargs)
|
||||
|
||||
self._palette = None
|
||||
|
||||
def do_set_property(self, pspec, value):
|
||||
if pspec.name == 'file-name':
|
||||
if self._buffer.file_name != value:
|
||||
self._buffer.file_name = value
|
||||
self.emit_paint_needed(0, 0, -1, -1)
|
||||
elif pspec.name == 'icon-name':
|
||||
if self._buffer.icon_name != value:
|
||||
self._buffer.icon_name = value
|
||||
self.emit_paint_needed(0, 0, -1, -1)
|
||||
elif pspec.name == 'xo-color':
|
||||
if self._buffer.xo_color != value:
|
||||
self._buffer.xo_color = value
|
||||
self.emit_paint_needed(0, 0, -1, -1)
|
||||
elif pspec.name == 'fill-color':
|
||||
if self._buffer.fill_color != value:
|
||||
self._buffer.fill_color = value
|
||||
self.emit_paint_needed(0, 0, -1, -1)
|
||||
elif pspec.name == 'stroke-color':
|
||||
if self._buffer.stroke_color != value:
|
||||
self._buffer.stroke_color = value
|
||||
self.emit_paint_needed(0, 0, -1, -1)
|
||||
elif pspec.name == 'size':
|
||||
if self._buffer.width != value:
|
||||
self._buffer.width = value
|
||||
self._buffer.height = value
|
||||
self.emit_request_changed()
|
||||
elif pspec.name == 'scale':
|
||||
if self._buffer.scale != value:
|
||||
self._buffer.scale = value
|
||||
self.emit_request_changed()
|
||||
elif pspec.name == 'cache':
|
||||
self._buffer.cache = value
|
||||
elif pspec.name == 'badge-name':
|
||||
if self._buffer.badge_name != value:
|
||||
self._buffer.badge_name = value
|
||||
self.emit_paint_needed(0, 0, -1, -1)
|
||||
|
||||
def do_get_property(self, pspec):
|
||||
if pspec.name == 'size':
|
||||
return self._buffer.width
|
||||
elif pspec.name == 'file-name':
|
||||
return self._buffer.file_name
|
||||
elif pspec.name == 'icon-name':
|
||||
return self._buffer.icon_name
|
||||
elif pspec.name == 'fill-color':
|
||||
return self._buffer.fill_color
|
||||
elif pspec.name == 'stroke-color':
|
||||
return self._buffer.stroke_color
|
||||
elif pspec.name == 'cache':
|
||||
return self._buffer.cache
|
||||
elif pspec.name == 'badge-name':
|
||||
return self._buffer.badge_name
|
||||
elif pspec.name == 'scale':
|
||||
return self._buffer.scale
|
||||
|
||||
def do_paint_below_children(self, cr, damaged_box):
|
||||
surface = self._buffer.get_surface()
|
||||
if surface:
|
||||
width, height = self.get_allocation()
|
||||
|
||||
x = (width - surface.get_width()) / 2
|
||||
y = (height - surface.get_height()) / 2
|
||||
|
||||
cr.set_source_surface(surface, x, y)
|
||||
cr.paint()
|
||||
|
||||
def do_get_content_width_request(self):
|
||||
surface = self._buffer.get_surface()
|
||||
if surface:
|
||||
size = surface.get_width()
|
||||
elif self._buffer.width:
|
||||
size = self._buffer.width
|
||||
else:
|
||||
size = 0
|
||||
|
||||
return size, size
|
||||
|
||||
def do_get_content_height_request(self, for_width):
|
||||
surface = self._buffer.get_surface()
|
||||
if surface:
|
||||
size = surface.get_height()
|
||||
elif self._buffer.height:
|
||||
size = self._buffer.height
|
||||
else:
|
||||
size = 0
|
||||
|
||||
return size, size
|
||||
|
||||
def do_button_press_event(self, event):
|
||||
self.emit_activated()
|
||||
return True
|
||||
|
||||
def get_palette(self):
|
||||
return self._palette
|
||||
|
||||
def set_palette(self, palette):
|
||||
if self._palette is not None:
|
||||
self._palette.props.invoker = None
|
||||
self._palette = palette
|
||||
if not self._palette.props.invoker:
|
||||
self._palette.props.invoker = CanvasInvoker(self)
|
||||
|
||||
def set_tooltip(self, text):
|
||||
self.set_palette(Palette(text))
|
||||
|
||||
palette = property(get_palette, set_palette)
|
||||
|
||||
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 icon_name
|
||||
|
||||
strength = strength + step
|
||||
@@ -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 _sugarext
|
||||
|
||||
ICON_ENTRY_PRIMARY = _sugarext.ICON_ENTRY_PRIMARY
|
||||
ICON_ENTRY_SECONDARY = _sugarext.ICON_ENTRY_SECONDARY
|
||||
|
||||
class IconEntry(_sugarext.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())
|
||||
_sugarext.IconEntry.set_icon(self, position, image)
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
# Copyright (C) 2007, Eduardo Silva <edsiper@gmail.com>
|
||||
#
|
||||
# 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.graphics.icon import Icon
|
||||
|
||||
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=icon_name, icon_size=gtk.ICON_SIZE_MENU)
|
||||
self.set_image(icon)
|
||||
icon.show()
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
"""Notebook class
|
||||
|
||||
This class create a gtk.Notebook() widget supporting
|
||||
a close button in every tab when the 'can-close-tabs' gproperty
|
||||
is enabled (True)
|
||||
"""
|
||||
|
||||
# Copyright (C) 2007, Eduardo Silva (edsiper@gmail.com)
|
||||
#
|
||||
# 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
|
||||
import gobject
|
||||
|
||||
class Notebook(gtk.Notebook):
|
||||
__gtype_name__ = 'SugarNotebook'
|
||||
|
||||
__gproperties__ = {
|
||||
'can-close-tabs': (bool, None, None, False,
|
||||
gobject.PARAM_READWRITE | gobject.PARAM_CONSTRUCT_ONLY)
|
||||
}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
# Initialise the Widget
|
||||
#
|
||||
# Side effects:
|
||||
# Set the 'can-close-tabs' property using **kwargs
|
||||
# Set True the scrollable notebook property
|
||||
|
||||
gobject.GObject.__init__(self, **kwargs)
|
||||
gtk.Notebook.__init__(self)
|
||||
|
||||
self.set_scrollable(True)
|
||||
self.show()
|
||||
|
||||
def do_set_property(self, pspec, value):
|
||||
if pspec.name == 'can-close-tabs':
|
||||
self._can_close_tabs = value
|
||||
else:
|
||||
raise AssertionError
|
||||
|
||||
def _add_icon_to_button(self, button):
|
||||
icon_box = gtk.HBox()
|
||||
image = gtk.Image()
|
||||
image.set_from_stock(gtk.STOCK_CLOSE, gtk.ICON_SIZE_MENU)
|
||||
gtk.Button.set_relief(button, gtk.RELIEF_NONE)
|
||||
|
||||
settings = gtk.Widget.get_settings(button)
|
||||
(w,h) = gtk.icon_size_lookup_for_settings(settings, gtk.ICON_SIZE_MENU)
|
||||
gtk.Widget.set_size_request(button, w + 4, h + 4)
|
||||
image.show()
|
||||
icon_box.pack_start(image, True, False, 0)
|
||||
button.add(icon_box)
|
||||
icon_box.show()
|
||||
|
||||
def _create_custom_tab(self, text, child):
|
||||
event_box = gtk.EventBox()
|
||||
|
||||
tab_box = gtk.HBox(False, 2)
|
||||
tab_label = gtk.Label(text)
|
||||
|
||||
tab_button = gtk.Button()
|
||||
tab_button.connect('clicked', self._close_page, child)
|
||||
|
||||
# Add a picture on a button
|
||||
self._add_icon_to_button(tab_button)
|
||||
icon_box = gtk.HBox(False, 0)
|
||||
|
||||
event_box.show()
|
||||
tab_button.show()
|
||||
tab_label.show()
|
||||
|
||||
tab_box.pack_start(tab_label, True)
|
||||
tab_box.pack_start(tab_button, True)
|
||||
|
||||
tab_box.show_all()
|
||||
event_box.add(tab_box)
|
||||
|
||||
return event_box
|
||||
|
||||
def add_page(self, text_label, widget):
|
||||
# Add a new page to the notebook
|
||||
if self._can_close_tabs:
|
||||
eventbox = self._create_custom_tab(text_label, widget)
|
||||
self.append_page(widget, eventbox)
|
||||
else:
|
||||
self.append_page(widget, gtk.Label(text_label))
|
||||
|
||||
pages = self.get_n_pages()
|
||||
|
||||
# Set the new page
|
||||
self.set_current_page(pages - 1)
|
||||
self.show_all()
|
||||
|
||||
return True
|
||||
|
||||
def _close_page(self, button, child):
|
||||
# Remove a page from the notebook
|
||||
page = self.page_num(child)
|
||||
|
||||
if page != -1:
|
||||
self.remove_page(page)
|
||||
@@ -0,0 +1,209 @@
|
||||
# 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 logging
|
||||
import time
|
||||
from gettext import gettext as _
|
||||
|
||||
import gtk
|
||||
import hippo
|
||||
|
||||
from sugar.bundle.activitybundle import ActivityBundle
|
||||
from sugar.graphics import style
|
||||
from sugar.graphics.icon import CanvasIcon
|
||||
from sugar.graphics.xocolor import XoColor
|
||||
from sugar.graphics.roundbox import CanvasRoundBox
|
||||
from sugar.datastore import datastore
|
||||
from sugar import activity
|
||||
from sugar.objects import objecttype
|
||||
|
||||
# TODO: Activities should request the Journal to open objectchooser dialogs. In
|
||||
# that way, we'll be able to reuse most of this code inside the Journal.
|
||||
|
||||
class ObjectChooser(gtk.Dialog):
|
||||
def __init__(self, title=None, parent=None, flags=0):
|
||||
gtk.Dialog.__init__(self, title, parent, flags, (gtk.STOCK_CANCEL,
|
||||
gtk.RESPONSE_REJECT, gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
|
||||
|
||||
self._jobjects = None
|
||||
self._query = {}
|
||||
self._selected_entry = False
|
||||
|
||||
self._box = hippo.CanvasBox()
|
||||
self._box.props.background_color = style.COLOR_PANEL_GREY.get_int()
|
||||
self._box.props.spacing = style.DEFAULT_SPACING
|
||||
self._box.props.padding = style.DEFAULT_SPACING
|
||||
|
||||
canvas = hippo.Canvas()
|
||||
canvas.set_root(self._box)
|
||||
|
||||
scrolled_window = gtk.ScrolledWindow()
|
||||
scrolled_window.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
|
||||
|
||||
scrolled_window.add_with_viewport(canvas)
|
||||
canvas.show()
|
||||
|
||||
self.vbox.add(scrolled_window)
|
||||
scrolled_window.show()
|
||||
|
||||
scrolled_window.props.shadow_type = gtk.SHADOW_NONE
|
||||
scrolled_window.get_child().props.shadow_type = gtk.SHADOW_NONE
|
||||
|
||||
self.refresh()
|
||||
|
||||
height = self.get_screen().get_height() * 3 / 4
|
||||
width = self.get_screen().get_width() * 3 / 4
|
||||
self.set_default_size(width, height)
|
||||
|
||||
def update_with_query(self, query):
|
||||
self._query = query
|
||||
self.refresh()
|
||||
|
||||
def refresh(self):
|
||||
logging.debug('ListView.refresh: %r' % self._query)
|
||||
self._jobjects, total_count = datastore.find(self._query, sorting=['-mtime'])
|
||||
self._update()
|
||||
|
||||
def _update(self):
|
||||
self._box.remove_all()
|
||||
for jobject in self._jobjects:
|
||||
entry_view = CollapsedEntry(jobject)
|
||||
entry_view.connect('button-release-event',
|
||||
self._entry_view_button_release_event_cb)
|
||||
self._box.append(entry_view)
|
||||
|
||||
def _entry_view_button_release_event_cb(self, entry_view, event):
|
||||
if self._selected_entry:
|
||||
self._selected_entry.set_selected(False)
|
||||
entry_view.set_selected(True)
|
||||
self._selected_entry = entry_view
|
||||
|
||||
def get_selected_object(self):
|
||||
if self._selected_entry:
|
||||
return self._selected_entry.jobject
|
||||
else:
|
||||
return None
|
||||
|
||||
class CollapsedEntry(CanvasRoundBox):
|
||||
_DATE_COL_WIDTH = style.zoom(100)
|
||||
_BUDDIES_COL_WIDTH = style.zoom(50)
|
||||
|
||||
def __init__(self, jobject):
|
||||
CanvasRoundBox.__init__(self)
|
||||
self.props.box_height = style.zoom(75)
|
||||
self.props.spacing = style.DEFAULT_SPACING
|
||||
self.props.border_color = style.COLOR_BLACK.get_int()
|
||||
self.props.background_color = style.COLOR_PANEL_GREY.get_int()
|
||||
|
||||
self.jobject = jobject
|
||||
self._icon_name = None
|
||||
|
||||
date = hippo.CanvasText(text=self._format_date(),
|
||||
xalign=hippo.ALIGNMENT_START,
|
||||
font_desc=style.FONT_NORMAL.get_pango_desc(),
|
||||
box_width=self._DATE_COL_WIDTH)
|
||||
self.append(date)
|
||||
|
||||
icon = CanvasIcon(icon_name=self._get_icon_name(),
|
||||
box_width=style.zoom(75))
|
||||
|
||||
if self.jobject.metadata.has_key('icon-color'):
|
||||
icon.props.xo_color = XoColor(self.jobject.metadata['icon-color'])
|
||||
|
||||
self.append(icon)
|
||||
|
||||
title = hippo.CanvasText(text=self._format_title(),
|
||||
xalign=hippo.ALIGNMENT_START,
|
||||
font_desc=style.FONT_BOLD.get_pango_desc(),
|
||||
size_mode=hippo.CANVAS_SIZE_WRAP_WORD)
|
||||
self.append(title)
|
||||
|
||||
def _get_icon_name(self):
|
||||
if self._icon_name:
|
||||
return self._icon_name
|
||||
|
||||
if self.jobject.is_activity_bundle():
|
||||
bundle = ActivityBundle(self.jobject.file_path)
|
||||
self._icon_name = bundle.get_icon()
|
||||
|
||||
if self.jobject.metadata['activity']:
|
||||
service_name = self.jobject.metadata['activity']
|
||||
activity_info = activity.get_registry().get_activity(service_name)
|
||||
if activity_info:
|
||||
self._icon_name = activity_info.icon
|
||||
|
||||
mime_type = self.jobject.metadata['mime_type']
|
||||
if not self._icon_name and mime_type:
|
||||
type = objecttype.get_registry().get_type_for_mime(mime_type)
|
||||
if type:
|
||||
self._icon_name = type.icon
|
||||
|
||||
if not self._icon_name:
|
||||
self._icon_name = 'image-missing'
|
||||
|
||||
return self._icon_name
|
||||
|
||||
def _format_date(self):
|
||||
""" Convert from a string in iso format to a more human-like format. """
|
||||
return _get_elapsed_string(self.jobject.metadata['mtime'])
|
||||
|
||||
def _format_title(self):
|
||||
return '"%s"' % self.jobject.metadata['title']
|
||||
|
||||
def set_selected(self, selected):
|
||||
if selected:
|
||||
self.props.border_color = style.COLOR_WHITE.get_int()
|
||||
self.props.background_color = style.COLOR_WHITE.get_int()
|
||||
else:
|
||||
self.props.border_color = style.COLOR_BLACK.get_int()
|
||||
self.props.background_color = style.COLOR_PANEL_GREY.get_int()
|
||||
|
||||
def _get_elapsed_string(date_string, max_levels=2):
|
||||
ti = time.strptime(date_string, "%Y-%m-%dT%H:%M:%S")
|
||||
|
||||
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],
|
||||
[_('%d second'), _('%d seconds'), 1]]
|
||||
levels = 0
|
||||
result = ''
|
||||
elapsed_seconds = int(time.time() - time.mktime(ti))
|
||||
for name_singular, name_plural, factor in units:
|
||||
elapsed_units = elapsed_seconds / factor
|
||||
if elapsed_units > 0:
|
||||
|
||||
if levels > 0:
|
||||
if max_levels - levels == 1:
|
||||
result += _(' and ')
|
||||
else:
|
||||
result += _(', ')
|
||||
|
||||
if elapsed_units == 1:
|
||||
result += name_singular % elapsed_units
|
||||
else:
|
||||
result += name_plural % elapsed_units
|
||||
elapsed_seconds -= elapsed_units * factor
|
||||
levels += 1
|
||||
|
||||
if levels == max_levels:
|
||||
break
|
||||
|
||||
return result
|
||||
|
||||
@@ -0,0 +1,722 @@
|
||||
# Copyright (C) 2007, Eduardo Silva <edsiper@gmail.com>
|
||||
#
|
||||
# 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 logging
|
||||
|
||||
import gtk
|
||||
import gobject
|
||||
import time
|
||||
import hippo
|
||||
|
||||
from sugar.graphics import palettegroup
|
||||
from sugar.graphics import animator
|
||||
from sugar.graphics import style
|
||||
from sugar import _sugarext
|
||||
|
||||
# Helper function to find the gap position and size of widget a
|
||||
def _calculate_gap(a, b):
|
||||
# Test for each side if the palette and invoker are
|
||||
# adjacent to each other.
|
||||
gap = True
|
||||
|
||||
if a.y + a.height == b.y:
|
||||
gap_side = gtk.POS_BOTTOM
|
||||
elif a.x + a.width == b.x:
|
||||
gap_side = gtk.POS_RIGHT
|
||||
elif a.x == b.x + b.width:
|
||||
gap_side = gtk.POS_LEFT
|
||||
elif a.y == b.y + b.height:
|
||||
gap_side = gtk.POS_TOP
|
||||
else:
|
||||
gap = False
|
||||
|
||||
if gap:
|
||||
if gap_side == gtk.POS_BOTTOM or gap_side == gtk.POS_TOP:
|
||||
gap_start = min(a.width, max(0, b.x - a.x))
|
||||
gap_size = max(0, min(a.width,
|
||||
(b.x + b.width) - a.x) - gap_start)
|
||||
elif gap_side == gtk.POS_RIGHT or gap_side == gtk.POS_LEFT:
|
||||
gap_start = min(a.height, max(0, b.y - a.y))
|
||||
gap_size = max(0, min(a.height,
|
||||
(b.y + b.height) - a.y) - gap_start)
|
||||
|
||||
if gap and gap_size > 0:
|
||||
return (gap_side, gap_start, gap_size)
|
||||
else:
|
||||
return False
|
||||
|
||||
class Palette(gtk.Window):
|
||||
PRIMARY = 0
|
||||
SECONDARY = 1
|
||||
|
||||
__gtype_name__ = 'SugarPalette'
|
||||
|
||||
__gproperties__ = {
|
||||
'invoker' : (object, None, None,
|
||||
gobject.PARAM_READWRITE)
|
||||
}
|
||||
|
||||
__gsignals__ = {
|
||||
'popup' : (gobject.SIGNAL_RUN_FIRST,
|
||||
gobject.TYPE_NONE, ([])),
|
||||
'popdown' : (gobject.SIGNAL_RUN_FIRST,
|
||||
gobject.TYPE_NONE, ([]))
|
||||
}
|
||||
|
||||
def __init__(self, label, accel_path=None, menu_after_content=False):
|
||||
gtk.Window.__init__(self)
|
||||
|
||||
self.set_decorated(False)
|
||||
self.set_resizable(False)
|
||||
# Just assume xthickness and ythickness are the same
|
||||
self.set_border_width(self.style.xthickness)
|
||||
self.connect('realize', self._realize_cb)
|
||||
|
||||
self.palette_state = self.PRIMARY
|
||||
|
||||
self._alignment = None
|
||||
self._old_alloc = None
|
||||
self._full_request = [0, 0]
|
||||
self._cursor_x = 0
|
||||
self._cursor_y = 0
|
||||
self._invoker = None
|
||||
self._group_id = None
|
||||
self._up = False
|
||||
self._palette_popup_sid = None
|
||||
|
||||
self._popup_anim = animator.Animator(0.3, 10)
|
||||
self._popup_anim.add(_PopupAnimation(self))
|
||||
|
||||
self._secondary_anim = animator.Animator(1.0, 10)
|
||||
self._secondary_anim.add(_SecondaryAnimation(self))
|
||||
|
||||
self._popdown_anim = animator.Animator(0.6, 10)
|
||||
self._popdown_anim.add(_PopdownAnimation(self))
|
||||
|
||||
vbox = gtk.VBox()
|
||||
|
||||
self._label = gtk.Label()
|
||||
self._label.set_size_request(-1, style.zoom(style.GRID_CELL_SIZE))
|
||||
self._label.set_alignment(0, 0.5)
|
||||
self._label.set_padding(style.zoom(15), 0)
|
||||
vbox.pack_start(self._label, False)
|
||||
|
||||
self._secondary_box = gtk.VBox()
|
||||
vbox.pack_start(self._secondary_box)
|
||||
|
||||
self._separator = gtk.HSeparator()
|
||||
self._secondary_box.pack_start(self._separator)
|
||||
|
||||
self._menu_content_separator = gtk.HSeparator()
|
||||
|
||||
if menu_after_content:
|
||||
self._add_content()
|
||||
self._secondary_box.pack_start(self._menu_content_separator)
|
||||
self._add_menu()
|
||||
else:
|
||||
self._add_menu()
|
||||
self._secondary_box.pack_start(self._menu_content_separator)
|
||||
self._add_content()
|
||||
|
||||
self.action_bar = PaletteActionBar()
|
||||
self._secondary_box.pack_start(self.action_bar)
|
||||
self.action_bar.show()
|
||||
|
||||
self.add(vbox)
|
||||
vbox.show()
|
||||
|
||||
# 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.set_primary_text(label, accel_path)
|
||||
self.set_group_id('default')
|
||||
|
||||
def _add_menu(self):
|
||||
self._menu_box = gtk.VBox()
|
||||
self._secondary_box.pack_start(self._menu_box)
|
||||
self._menu_box.show()
|
||||
|
||||
def _add_content(self):
|
||||
# The content is not shown until a widget is added
|
||||
self._content = gtk.VBox()
|
||||
self._content.set_border_width(style.zoom(15))
|
||||
self._secondary_box.pack_start(self._content)
|
||||
|
||||
def do_style_set(self, previous_style):
|
||||
# Prevent a warning from pygtk
|
||||
if previous_style is not None:
|
||||
gtk.Window.do_style_set(self, previous_style)
|
||||
self.set_border_width(self.style.xthickness)
|
||||
|
||||
def is_up(self):
|
||||
return self._up
|
||||
|
||||
def get_rect(self):
|
||||
win_x, win_y = self.window.get_origin()
|
||||
rectangle = self.get_allocation()
|
||||
|
||||
x = win_x + rectangle.x
|
||||
y = win_y + rectangle.y
|
||||
width = rectangle.width
|
||||
height = rectangle.height
|
||||
|
||||
return gtk.gdk.Rectangle(x, y, width, height)
|
||||
|
||||
def set_primary_text(self, label, accel_path=None):
|
||||
if label is not None:
|
||||
self._label.set_markup("<b>"+label+"</b>")
|
||||
self._label.show()
|
||||
|
||||
def set_content(self, widget):
|
||||
if len(self._content.get_children()) > 0:
|
||||
self._content.remove(self._content.get_children()[0])
|
||||
|
||||
if widget is not None:
|
||||
self._content.add(widget)
|
||||
self._content.show()
|
||||
else:
|
||||
self._content.hide()
|
||||
|
||||
self._update_accept_focus()
|
||||
self._update_separators()
|
||||
|
||||
def set_group_id(self, group_id):
|
||||
if self._group_id:
|
||||
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)
|
||||
|
||||
def do_set_property(self, pspec, value):
|
||||
if pspec.name == 'invoker':
|
||||
if self._invoker is not None:
|
||||
self._invoker.disconnect(self._enter_invoker_hid)
|
||||
self._invoker.disconnect(self._leave_invoker_hid)
|
||||
|
||||
self._invoker = value
|
||||
if value is not None:
|
||||
self._enter_invoker_hid = self._invoker.connect(
|
||||
'mouse-enter', self._invoker_mouse_enter_cb)
|
||||
self._leave_invoker_hid = self._invoker.connect(
|
||||
'mouse-leave', self._invoker_mouse_leave_cb)
|
||||
else:
|
||||
raise AssertionError
|
||||
|
||||
def do_get_property(self, pspec):
|
||||
if pspec.name == 'invoker':
|
||||
return self._invoker
|
||||
else:
|
||||
raise AssertionError
|
||||
|
||||
def do_size_request(self, requisition):
|
||||
gtk.Window.do_size_request(self, requisition)
|
||||
|
||||
requisition.width = max(requisition.width, self._full_request[0])
|
||||
|
||||
# Minimum width
|
||||
requisition.width = max(requisition.width,
|
||||
style.zoom(style.GRID_CELL_SIZE*2))
|
||||
|
||||
def do_size_allocate(self, allocation):
|
||||
gtk.Window.do_size_allocate(self, allocation)
|
||||
|
||||
if self._old_alloc is None or \
|
||||
self._old_alloc.x != allocation.x or \
|
||||
self._old_alloc.y != allocation.y or \
|
||||
self._old_alloc.width != allocation.width or \
|
||||
self._old_alloc.height != allocation.height:
|
||||
self.queue_draw()
|
||||
|
||||
# We need to store old allocation because when size_allocate
|
||||
# is called widget.allocation is already updated.
|
||||
# gtk.Window resizing is different from normal containers:
|
||||
# the X window is resized, widget.allocation is updated from
|
||||
# the configure request handler and finally size_allocate is called.
|
||||
self._old_alloc = allocation
|
||||
|
||||
def do_expose_event(self, event):
|
||||
# We want to draw a border with a beautiful gap
|
||||
if self._invoker is not None and self._invoker.has_rectangle_gap():
|
||||
invoker = self._invoker.get_rect()
|
||||
palette = self.get_rect()
|
||||
|
||||
gap = _calculate_gap(palette, invoker)
|
||||
else:
|
||||
gap = False
|
||||
|
||||
if gap:
|
||||
self.style.paint_box_gap(event.window, gtk.STATE_PRELIGHT,
|
||||
gtk.SHADOW_IN, event.area, self, "palette",
|
||||
0, 0,
|
||||
self.allocation.width,
|
||||
self.allocation.height,
|
||||
gap[0], gap[1], gap[2])
|
||||
else:
|
||||
self.style.paint_box(event.window, gtk.STATE_PRELIGHT,
|
||||
gtk.SHADOW_IN, event.area, self, "palette",
|
||||
0, 0,
|
||||
self.allocation.width,
|
||||
self.allocation.height)
|
||||
|
||||
# Fall trough to the container expose handler.
|
||||
# (Leaving out the window expose handler which redraws everything)
|
||||
gtk.Bin.do_expose_event(self, event)
|
||||
|
||||
def _update_separators(self):
|
||||
visible = len(self.menu.get_children()) > 0 or \
|
||||
len(self._content.get_children()) > 0
|
||||
self._separator.props.visible = visible
|
||||
|
||||
visible = len(self.menu.get_children()) > 0 and \
|
||||
len(self._content.get_children()) > 0
|
||||
self._menu_content_separator.props.visible = visible
|
||||
|
||||
def _update_accept_focus(self):
|
||||
accept_focus = len(self._content.get_children())
|
||||
if self.window:
|
||||
self.window.set_accept_focus(accept_focus)
|
||||
|
||||
def _realize_cb(self, widget):
|
||||
self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
|
||||
self._update_accept_focus()
|
||||
|
||||
def _update_full_request(self):
|
||||
state = self.palette_state
|
||||
|
||||
self._set_state(self.SECONDARY)
|
||||
self._full_request = self.size_request()
|
||||
|
||||
self._set_state(state)
|
||||
|
||||
def _update_position(self):
|
||||
invoker = self._invoker
|
||||
if invoker is None or self._alignment is None:
|
||||
logging.error('Cannot update the palette position.')
|
||||
return
|
||||
|
||||
rect = self.size_request()
|
||||
position = invoker.get_position_for_alignment(self._alignment, rect)
|
||||
if position is None:
|
||||
position = invoker.get_position(rect)
|
||||
|
||||
self.move(position.x, position.y)
|
||||
|
||||
def _show(self):
|
||||
if self._up:
|
||||
return
|
||||
|
||||
self._palette_popup_sid = _palette_observer.connect(
|
||||
'popup', self._palette_observer_popup_cb)
|
||||
|
||||
if self._invoker is not None:
|
||||
self._update_full_request()
|
||||
self._alignment = self._invoker.get_alignment(self._full_request)
|
||||
self._update_position()
|
||||
|
||||
self.menu.set_active(True)
|
||||
self.show()
|
||||
|
||||
self._invoker.notify_popup()
|
||||
|
||||
self._up = True
|
||||
_palette_observer.emit('popup', self)
|
||||
self.emit('popup')
|
||||
|
||||
def _hide(self):
|
||||
self._secondary_anim.stop()
|
||||
|
||||
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, immediate=False):
|
||||
self._popdown_anim.stop()
|
||||
|
||||
if not immediate:
|
||||
self._popup_anim.start()
|
||||
else:
|
||||
self._show()
|
||||
|
||||
self._secondary_anim.start()
|
||||
|
||||
def popdown(self, immediate=False):
|
||||
self._popup_anim.stop()
|
||||
|
||||
if not immediate:
|
||||
self._popdown_anim.start()
|
||||
else:
|
||||
self._hide()
|
||||
|
||||
def _set_state(self, state):
|
||||
if self.palette_state == state:
|
||||
return
|
||||
|
||||
if state == self.PRIMARY:
|
||||
self.menu.unembed()
|
||||
self._secondary_box.hide()
|
||||
elif state == self.SECONDARY:
|
||||
self.menu.embed(self._menu_box)
|
||||
self._secondary_box.show()
|
||||
|
||||
self.palette_state = state
|
||||
|
||||
def _invoker_mouse_enter_cb(self, invoker):
|
||||
immediate = False
|
||||
|
||||
if self.is_up():
|
||||
self._popdown_anim.stop()
|
||||
return
|
||||
|
||||
if self._group_id:
|
||||
group = palettegroup.get_group(self._group_id)
|
||||
if group and group.is_up():
|
||||
self._set_state(group.get_state())
|
||||
|
||||
immediate = True
|
||||
group.popdown()
|
||||
|
||||
self.popup(immediate=immediate)
|
||||
|
||||
def _invoker_mouse_leave_cb(self, invoker):
|
||||
self.popdown()
|
||||
|
||||
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):
|
||||
if event.detail != gtk.gdk.NOTIFY_INFERIOR:
|
||||
self.popdown()
|
||||
|
||||
def _palette_observer_popup_cb(self, observer, palette):
|
||||
if self != palette:
|
||||
self._hide()
|
||||
|
||||
class PaletteActionBar(gtk.HButtonBox):
|
||||
def add_action(label, icon_name=None):
|
||||
button = Button(label)
|
||||
|
||||
if icon_name:
|
||||
icon = Icon(icon_name)
|
||||
button.set_image(icon)
|
||||
icon.show()
|
||||
|
||||
self.pack_start(button)
|
||||
button.show()
|
||||
|
||||
class _Menu(_sugarext.Menu):
|
||||
__gtype_name__ = 'SugarPaletteMenu'
|
||||
|
||||
def __init__(self, palette):
|
||||
_sugarext.Menu.__init__(self)
|
||||
self._palette = palette
|
||||
|
||||
def do_insert(self, item, position):
|
||||
_sugarext.Menu.do_insert(self, item, position)
|
||||
self._palette._update_separators()
|
||||
self.show()
|
||||
|
||||
def do_expose_event(self, event):
|
||||
# Ignore the Menu expose, just do the MenuShell expose to prevent any
|
||||
# border from being drawn here. A border is drawn by the palette object
|
||||
# around everything.
|
||||
gtk.MenuShell.do_expose_event(self, event)
|
||||
|
||||
def do_grab_notify(self, was_grabbed):
|
||||
# Ignore grab_notify as the menu would close otherwise
|
||||
pass
|
||||
|
||||
def do_deactivate(self):
|
||||
self._palette._hide()
|
||||
|
||||
class _PopupAnimation(animator.Animation):
|
||||
def __init__(self, palette):
|
||||
animator.Animation.__init__(self, 0.0, 1.0)
|
||||
self._palette = palette
|
||||
|
||||
def next_frame(self, current):
|
||||
if current == 1.0:
|
||||
self._palette._set_state(Palette.PRIMARY)
|
||||
self._palette._show()
|
||||
|
||||
class _SecondaryAnimation(animator.Animation):
|
||||
def __init__(self, palette):
|
||||
animator.Animation.__init__(self, 0.0, 1.0)
|
||||
self._palette = palette
|
||||
|
||||
def next_frame(self, current):
|
||||
if current == 1.0:
|
||||
self._palette._set_state(Palette.SECONDARY)
|
||||
self._palette._update_position()
|
||||
|
||||
class _PopdownAnimation(animator.Animation):
|
||||
def __init__(self, palette):
|
||||
animator.Animation.__init__(self, 0.0, 1.0)
|
||||
self._palette = palette
|
||||
|
||||
def next_frame(self, current):
|
||||
if current == 1.0:
|
||||
self._palette._hide()
|
||||
|
||||
class Invoker(gobject.GObject):
|
||||
__gtype_name__ = 'SugarPaletteInvoker'
|
||||
|
||||
__gsignals__ = {
|
||||
'mouse-enter': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])),
|
||||
'mouse-leave': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])),
|
||||
'focus-out': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([]))
|
||||
}
|
||||
|
||||
ANCHORED = 0
|
||||
AT_CURSOR = 1
|
||||
|
||||
BOTTOM = [(0.0, 0.0, 0.0, 1.0),
|
||||
(-1.0, 0.0, 1.0, 1.0)]
|
||||
RIGHT = [(0.0, 0.0, 1.0, 0.0),
|
||||
(0.0, -1.0, 1.0, 1.0)]
|
||||
TOP = [(0.0, -1.0, 0.0, 0.0),
|
||||
(-1.0, -1.0, 1.0, 0.0)]
|
||||
LEFT = [(-1.0, 0.0, 0.0, 0.0),
|
||||
(-1.0, -1.0, 0.0, 1.0)]
|
||||
|
||||
def __init__(self):
|
||||
gobject.GObject.__init__(self)
|
||||
|
||||
self._screen_area = gtk.gdk.Rectangle(0, 0, gtk.gdk.screen_width(),
|
||||
gtk.gdk.screen_height())
|
||||
self._position_hint = self.ANCHORED
|
||||
self._cursor_x = -1
|
||||
self._cursor_y = -1
|
||||
|
||||
def _get_position_for_alignment(self, alignment, palette_dim):
|
||||
palette_halign = alignment[0]
|
||||
palette_valign = alignment[1]
|
||||
invoker_halign = alignment[2]
|
||||
invoker_valign = alignment[3]
|
||||
|
||||
if self._cursor_x == -1 or self._cursor_y == -1:
|
||||
display = gtk.gdk.display_get_default()
|
||||
screen, x, y, mask = display.get_pointer()
|
||||
self._cursor_x = x
|
||||
self._cursor_y = y
|
||||
|
||||
if self._position_hint is self.ANCHORED:
|
||||
rect = self.get_rect()
|
||||
else:
|
||||
dist = style.PALETTE_CURSOR_DISTANCE
|
||||
rect = gtk.gdk.Rectangle(self._cursor_x - dist,
|
||||
self._cursor_y - dist,
|
||||
dist * 2, dist * 2)
|
||||
|
||||
palette_width, palette_height = palette_dim
|
||||
|
||||
x = rect.x + rect.width * invoker_halign + \
|
||||
palette_width * palette_halign
|
||||
|
||||
y = rect.y + rect.height * invoker_valign + \
|
||||
palette_height * palette_valign
|
||||
|
||||
return gtk.gdk.Rectangle(int(x), int(y),
|
||||
palette_width, palette_height)
|
||||
|
||||
def _in_screen(self, rect):
|
||||
return rect.x >= self._screen_area.x and \
|
||||
rect.y >= self._screen_area.y and \
|
||||
rect.x + rect.width <= self._screen_area.width and \
|
||||
rect.y + rect.height <= self._screen_area.height
|
||||
|
||||
def _get_alignments(self):
|
||||
if self._position_hint is self.AT_CURSOR:
|
||||
return [(0.0, 0.0, 1.0, 1.0),
|
||||
(0.0, -1.0, 1.0, 0.0),
|
||||
(-1.0, -1.0, 0.0, 0.0),
|
||||
(-1.0, 0.0, 0.0, 1.0)]
|
||||
else:
|
||||
return self.BOTTOM + self.RIGHT + self.TOP + self.LEFT
|
||||
|
||||
def get_position_for_alignment(self, alignment, palette_dim):
|
||||
rect = self._get_position_for_alignment(alignment, palette_dim)
|
||||
if self._in_screen(rect):
|
||||
return rect
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_position(self, palette_dim):
|
||||
for alignment in self._get_alignments():
|
||||
rect = self._get_position_for_alignment(alignment, palette_dim)
|
||||
if self._in_screen(rect):
|
||||
break
|
||||
|
||||
return rect
|
||||
|
||||
def get_alignment(self, palette_dim):
|
||||
for alignment in self._get_alignments():
|
||||
rect = self._get_position_for_alignment(alignment, palette_dim)
|
||||
if self._in_screen(rect):
|
||||
break
|
||||
|
||||
return alignment
|
||||
|
||||
def has_rectangle_gap(self):
|
||||
return False
|
||||
|
||||
def draw_rectangle(self, event, palette):
|
||||
pass
|
||||
|
||||
def notify_popup(self):
|
||||
pass
|
||||
|
||||
def notify_popdown(self):
|
||||
self._cursor_x = -1
|
||||
self._cursor_y = -1
|
||||
|
||||
class WidgetInvoker(Invoker):
|
||||
def __init__(self, widget):
|
||||
Invoker.__init__(self)
|
||||
self._widget = widget
|
||||
|
||||
widget.connect('enter-notify-event', self._enter_notify_event_cb)
|
||||
widget.connect('leave-notify-event', self._leave_notify_event_cb)
|
||||
|
||||
def get_rect(self):
|
||||
win_x, win_y = self._widget.window.get_origin()
|
||||
rectangle = self._widget.get_allocation()
|
||||
|
||||
x = win_x + rectangle.x
|
||||
y = win_y + rectangle.y
|
||||
width = rectangle.width
|
||||
height = rectangle.height
|
||||
|
||||
return gtk.gdk.Rectangle(x, y, width, height)
|
||||
|
||||
def has_rectangle_gap(self):
|
||||
return True
|
||||
|
||||
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,
|
||||
gap[0], gap[1], gap[2])
|
||||
else:
|
||||
style.paint_box(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)
|
||||
|
||||
def _enter_notify_event_cb(self, widget, event):
|
||||
self.emit('mouse-enter')
|
||||
|
||||
def _leave_notify_event_cb(self, widget, event):
|
||||
self.emit('mouse-leave')
|
||||
|
||||
def get_toplevel(self):
|
||||
return self._widget.get_toplevel()
|
||||
|
||||
def notify_popup(self):
|
||||
Invoker.notify_popup(self)
|
||||
self._widget.queue_draw()
|
||||
|
||||
def notify_popdown(self):
|
||||
Invoker.notify_popdown(self)
|
||||
self._widget.queue_draw()
|
||||
|
||||
class CanvasInvoker(Invoker):
|
||||
def __init__(self, item):
|
||||
Invoker.__init__(self)
|
||||
|
||||
self._item = item
|
||||
self._position_hint = self.AT_CURSOR
|
||||
|
||||
item.connect('motion-notify-event',
|
||||
self._motion_notify_event_cb)
|
||||
|
||||
def get_default_position(self):
|
||||
return self.AT_CURSOR
|
||||
|
||||
def get_rect(self):
|
||||
context = self._item.get_context()
|
||||
if context:
|
||||
x, y = context.translate_to_screen(self._item)
|
||||
width, height = self._item.get_allocation()
|
||||
return gtk.gdk.Rectangle(x, y, width, height)
|
||||
else:
|
||||
return gtk.gdk.Rectangle()
|
||||
|
||||
def _motion_notify_event_cb(self, button, event):
|
||||
if event.detail == hippo.MOTION_DETAIL_ENTER:
|
||||
context = self._item.get_context()
|
||||
self.emit('mouse-enter')
|
||||
elif event.detail == hippo.MOTION_DETAIL_LEAVE:
|
||||
self.emit('mouse-leave')
|
||||
|
||||
return False
|
||||
|
||||
def get_toplevel(self):
|
||||
return hippo.get_canvas_for_item(self._item).get_toplevel()
|
||||
|
||||
class ToolInvoker(WidgetInvoker):
|
||||
def __init__(self, widget):
|
||||
WidgetInvoker.__init__(self, widget.child)
|
||||
|
||||
def _get_alignments(self):
|
||||
parent = self._widget.get_parent()
|
||||
if parent is None:
|
||||
return WidgetInvoker.get_alignments()
|
||||
|
||||
if parent.get_orientation() is gtk.ORIENTATION_HORIZONTAL:
|
||||
return self.BOTTOM + self.TOP
|
||||
else:
|
||||
return self.LEFT + self.RIGHT
|
||||
|
||||
class _PaletteObserver(gobject.GObject):
|
||||
__gtype_name__ = 'SugarPaletteObserver'
|
||||
|
||||
__gsignals__ = {
|
||||
'popup': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([object]))
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
gobject.GObject.__init__(self)
|
||||
|
||||
_palette_observer = _PaletteObserver()
|
||||
@@ -0,0 +1,90 @@
|
||||
# Copyright (C) 2007 Red Hat, Inc.
|
||||
#
|
||||
# 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 gobject
|
||||
|
||||
_groups = {}
|
||||
|
||||
def get_group(group_id):
|
||||
if _groups.has_key(group_id):
|
||||
group = _groups[group_id]
|
||||
else:
|
||||
group = Group()
|
||||
_groups[group_id] = group
|
||||
|
||||
return group
|
||||
|
||||
class Group(gobject.GObject):
|
||||
__gsignals__ = {
|
||||
'popup' : (gobject.SIGNAL_RUN_FIRST,
|
||||
gobject.TYPE_NONE, ([])),
|
||||
'popdown' : (gobject.SIGNAL_RUN_FIRST,
|
||||
gobject.TYPE_NONE, ([]))
|
||||
}
|
||||
def __init__(self):
|
||||
gobject.GObject.__init__(self)
|
||||
self._up = False
|
||||
self._palettes = []
|
||||
self._sig_ids = {}
|
||||
|
||||
def is_up(self):
|
||||
return self._up
|
||||
|
||||
def get_state(self):
|
||||
for palette in self._palettes:
|
||||
if palette.is_up():
|
||||
return palette.palette_state
|
||||
|
||||
return None
|
||||
|
||||
def add(self, palette):
|
||||
self._palettes.append(palette)
|
||||
|
||||
self._sig_ids[palette] = []
|
||||
|
||||
sid = palette.connect('popup', self._palette_popup_cb)
|
||||
self._sig_ids[palette].append(sid)
|
||||
|
||||
sid = palette.connect('popdown', self._palette_popdown_cb)
|
||||
self._sig_ids[palette].append(sid)
|
||||
|
||||
def remove(self, palette):
|
||||
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')
|
||||
self._up = True
|
||||
|
||||
def _palette_popdown_cb(self, palette):
|
||||
down = True
|
||||
for palette in self._palettes:
|
||||
if palette.is_up():
|
||||
down = False
|
||||
|
||||
if down:
|
||||
self._up = False
|
||||
self.emit('popdown')
|
||||
@@ -0,0 +1,23 @@
|
||||
# Copyright (C) 2007, Red Hat, Inc.
|
||||
#
|
||||
# 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
|
||||
|
||||
class Panel(gtk.VBox):
|
||||
__gtype_name__ = 'SugarPanel'
|
||||
def __init__(self):
|
||||
gtk.VBox.__init__(self)
|
||||
@@ -0,0 +1,67 @@
|
||||
# Copyright (C) 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
|
||||
# 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.graphics.icon import Icon
|
||||
from sugar.graphics.palette import Palette, ToolInvoker
|
||||
|
||||
class RadioToolButton(gtk.RadioToolButton):
|
||||
__gtype_name__ = "SugarRadioToolButton"
|
||||
|
||||
def __init__(self, named_icon=None, group=None, xo_color=None):
|
||||
gtk.RadioToolButton.__init__(self, group=group)
|
||||
self._palette = None
|
||||
self._xo_color = xo_color
|
||||
self.set_named_icon(named_icon)
|
||||
|
||||
def set_named_icon(self, named_icon):
|
||||
icon = Icon(icon_name=named_icon,
|
||||
xo_color=self._xo_color,
|
||||
icon_size=gtk.ICON_SIZE_LARGE_TOOLBAR)
|
||||
self.set_icon_widget(icon)
|
||||
icon.show()
|
||||
|
||||
def get_palette(self):
|
||||
return self._palette
|
||||
|
||||
def set_palette(self, palette):
|
||||
if self._palette is not None:
|
||||
self._palette.props.invoker = None
|
||||
self._palette = palette
|
||||
self._palette.props.invoker = ToolInvoker(self)
|
||||
|
||||
def set_tooltip(self, text):
|
||||
self.set_palette(Palette(text))
|
||||
|
||||
def do_expose_event(self, event):
|
||||
if self._palette and self._palette.is_up():
|
||||
invoker = self._palette.props.invoker
|
||||
invoker.draw_rectangle(event, self._palette)
|
||||
elif self.child.state == gtk.STATE_PRELIGHT:
|
||||
self.child.style.paint_box(event.window, gtk.STATE_PRELIGHT,
|
||||
gtk.SHADOW_NONE, event.area,
|
||||
self.child, "toolbutton-prelight",
|
||||
self.allocation.x,
|
||||
self.allocation.y,
|
||||
self.allocation.width,
|
||||
self.allocation.height)
|
||||
|
||||
gtk.RadioToolButton.do_expose_event(self, event)
|
||||
|
||||
palette = property(get_palette, set_palette)
|
||||
@@ -0,0 +1,66 @@
|
||||
# Copyright (C) 2006-2007 Red Hat, Inc.
|
||||
#
|
||||
# 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 math
|
||||
|
||||
import hippo
|
||||
|
||||
from sugar.graphics import style
|
||||
|
||||
class CanvasRoundBox(hippo.CanvasBox, hippo.CanvasItem):
|
||||
__gtype_name__ = 'SugarRoundBox'
|
||||
|
||||
_BORDER_DEFAULT = style.LINE_WIDTH
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
hippo.CanvasBox.__init__(self, **kwargs)
|
||||
|
||||
# TODO: we should calculate this value depending on the height of the box.
|
||||
self._radius = style.zoom(10)
|
||||
|
||||
self.props.orientation = hippo.ORIENTATION_HORIZONTAL
|
||||
self.props.border = self._BORDER_DEFAULT
|
||||
self.props.border_left = self._radius
|
||||
self.props.border_right = self._radius
|
||||
self.props.border_color = style.COLOR_BLACK.get_int()
|
||||
|
||||
def do_paint_background(self, cr, damaged_box):
|
||||
[width, height] = self.get_allocation()
|
||||
|
||||
x = self._BORDER_DEFAULT / 2
|
||||
y = self._BORDER_DEFAULT / 2
|
||||
width -= self._BORDER_DEFAULT
|
||||
height -= self._BORDER_DEFAULT
|
||||
|
||||
cr.move_to(x + self._radius, y);
|
||||
cr.arc(x + width - self._radius, y + self._radius,
|
||||
self._radius, math.pi * 1.5, math.pi * 2);
|
||||
cr.arc(x + width - self._radius, x + height - self._radius,
|
||||
self._radius, 0, math.pi * 0.5);
|
||||
cr.arc(x + self._radius, y + height - self._radius,
|
||||
self._radius, math.pi * 0.5, math.pi);
|
||||
cr.arc(x + self._radius, y + self._radius, self._radius,
|
||||
math.pi, math.pi * 1.5);
|
||||
|
||||
hippo.cairo_set_source_rgba32(cr, self.props.background_color)
|
||||
cr.fill_preserve();
|
||||
|
||||
# TODO: we should be more consistent here with the border properties.
|
||||
if self.props.border_color:
|
||||
hippo.cairo_set_source_rgba32(cr, self.props.border_color)
|
||||
cr.set_line_width(self.props.border_top)
|
||||
cr.stroke()
|
||||
@@ -0,0 +1,239 @@
|
||||
# Copyright (C) 2007 Red Hat, Inc.
|
||||
#
|
||||
# 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.
|
||||
|
||||
from numpy import array
|
||||
from random import random
|
||||
|
||||
import hippo
|
||||
import gobject
|
||||
import gtk
|
||||
|
||||
_PLACE_TRIALS = 20
|
||||
_MAX_WEIGHT = 255
|
||||
_CELL_SIZE = 4
|
||||
|
||||
class _Grid(gobject.GObject):
|
||||
__gsignals__ = {
|
||||
'child-changed' : (gobject.SIGNAL_RUN_FIRST,
|
||||
gobject.TYPE_NONE,
|
||||
([gobject.TYPE_PYOBJECT]))
|
||||
}
|
||||
def __init__(self, width, height):
|
||||
gobject.GObject.__init__(self)
|
||||
|
||||
self.width = width
|
||||
self.height = height
|
||||
self._children = []
|
||||
self._collisions = []
|
||||
self._collisions_sid = 0
|
||||
|
||||
self._array = array([0], dtype='b')
|
||||
self._array.resize(width * height)
|
||||
|
||||
def add(self, child, width, height):
|
||||
trials = _PLACE_TRIALS
|
||||
weight = _MAX_WEIGHT
|
||||
while trials > 0 and weight:
|
||||
x = int(random() * (self.width - width))
|
||||
y = int(random() * (self.height - height))
|
||||
|
||||
rect = gtk.gdk.Rectangle(x, y, width, height)
|
||||
new_weight = self._compute_weight(rect)
|
||||
if weight > new_weight:
|
||||
weight = new_weight
|
||||
|
||||
trials -= 1
|
||||
|
||||
child.grid_rect = rect
|
||||
child.locked = False
|
||||
|
||||
self._add_child(child)
|
||||
|
||||
if weight > 0:
|
||||
self._detect_collisions(child)
|
||||
|
||||
def remove(self, child):
|
||||
self._children.remove(child)
|
||||
self._remove_weight(child.grid_rect)
|
||||
child.grid_rect = None
|
||||
|
||||
def _add_child(self, child):
|
||||
self._children.append(child)
|
||||
self.add_weight(child.grid_rect)
|
||||
|
||||
def _move_child(self, child, new_rect):
|
||||
self._remove_weight(child.grid_rect)
|
||||
self.add_weight(new_rect)
|
||||
|
||||
child.grid_rect = new_rect
|
||||
|
||||
self.emit('child-changed', child)
|
||||
|
||||
def _shift_child(self, child):
|
||||
rect = child.grid_rect
|
||||
weight = self._compute_weight(rect)
|
||||
new_rects = []
|
||||
|
||||
if (rect.x + rect.width < self.width - 1):
|
||||
new_rects.append(gtk.gdk.Rectangle(rect.x + 1, rect.y,
|
||||
rect.width, rect.height))
|
||||
|
||||
if (rect.x - 1 > 0):
|
||||
new_rects.append(gtk.gdk.Rectangle(rect.x - 1, rect.y,
|
||||
rect.width, rect.height))
|
||||
|
||||
if (rect.y + rect.height < self.height - 1):
|
||||
new_rects.append(gtk.gdk.Rectangle(rect.x, rect.y + 1,
|
||||
rect.width, rect.height))
|
||||
|
||||
if (rect.y - 1 > 0):
|
||||
new_rects.append(gtk.gdk.Rectangle(rect.x, rect.y - 1,
|
||||
rect.width, rect.height))
|
||||
|
||||
best_rect = None
|
||||
for new_rect in new_rects:
|
||||
new_weight = self._compute_weight(new_rect)
|
||||
if new_weight < weight:
|
||||
best_rect = new_rect
|
||||
weight = new_weight
|
||||
|
||||
if best_rect:
|
||||
self._move_child(child, best_rect)
|
||||
|
||||
return weight
|
||||
|
||||
|
||||
def _solve_collisions(self):
|
||||
for collision in self._collisions[:]:
|
||||
weight = self._shift_child(collision)
|
||||
if not weight:
|
||||
self._collisions.remove(collision)
|
||||
|
||||
return (len(self._collisions) > 0)
|
||||
|
||||
def _detect_collisions(self, child):
|
||||
collision_found = False
|
||||
for c in self._children:
|
||||
intersection = child.grid_rect.intersect(c.grid_rect)
|
||||
if c != child and intersection.width > 0:
|
||||
if c not in self._collisions:
|
||||
collision_found = True
|
||||
self._collisions.append(c)
|
||||
|
||||
if collision_found:
|
||||
if child not in self._collisions:
|
||||
self._collisions.append(child)
|
||||
|
||||
# if len(self._collisions) and not self._collisions_sid:
|
||||
# self._collisions_sid = gobject.idle_add(self._solve_collisions)
|
||||
|
||||
def add_weight(self, rect):
|
||||
for i in range(rect.x, rect.x + rect.width):
|
||||
for j in range(rect.y, rect.y + rect.height):
|
||||
self[j, i] += 1
|
||||
|
||||
def _remove_weight(self, rect):
|
||||
for i in range(rect.x, rect.x + rect.width):
|
||||
for j in range(rect.y, rect.y + rect.height):
|
||||
self[j, i] -= 1
|
||||
|
||||
def _compute_weight(self, rect):
|
||||
weight = 0
|
||||
|
||||
for i in range(rect.x, rect.x + rect.width):
|
||||
for j in range(rect.y, rect.y + rect.height):
|
||||
weight += self[j, i]
|
||||
|
||||
return weight
|
||||
|
||||
def __getitem__(self, (row, col)):
|
||||
return self._array[col + row * self.width]
|
||||
|
||||
def __setitem__(self, (row, col), value):
|
||||
self._array[col + row * self.width] = value
|
||||
|
||||
|
||||
class SpreadLayout(gobject.GObject,hippo.CanvasLayout):
|
||||
__gtype_name__ = 'SugarSpreadLayout'
|
||||
def __init__(self):
|
||||
gobject.GObject.__init__(self)
|
||||
|
||||
min_width, width = self.do_get_width_request()
|
||||
min_height, height = self.do_get_height_request(width)
|
||||
|
||||
self._grid = _Grid(width / _CELL_SIZE, height / _CELL_SIZE)
|
||||
self._grid.connect('child-changed', self._grid_child_changed_cb)
|
||||
|
||||
def add_center(self, child):
|
||||
self._box.append(child)
|
||||
|
||||
width, height = self._get_child_grid_size(child)
|
||||
rect = gtk.gdk.Rectangle(int((self._grid.width - width) / 2),
|
||||
int((self._grid.height - height) / 2),
|
||||
width + 1, height + 1)
|
||||
self._grid.add_weight(rect)
|
||||
|
||||
box_child = self._box.find_box_child(child)
|
||||
box_child.grid_rect = None
|
||||
|
||||
def add(self, child):
|
||||
self._box.append(child)
|
||||
|
||||
width, height = self._get_child_grid_size(child)
|
||||
box_child = self._box.find_box_child(child)
|
||||
self._grid.add(box_child, width, height)
|
||||
|
||||
def remove(self, child):
|
||||
box_child = self._box.find_box_child(child)
|
||||
self._grid.remove(box_child)
|
||||
|
||||
self._box.remove(child)
|
||||
|
||||
def do_set_box(self, box):
|
||||
self._box = box
|
||||
|
||||
def do_get_height_request(self, for_width):
|
||||
return 0, gtk.gdk.screen_height()
|
||||
|
||||
def do_get_width_request(self):
|
||||
return 0, gtk.gdk.screen_width()
|
||||
|
||||
def do_allocate(self, x, y, width, height,
|
||||
req_width, req_height, origin_changed):
|
||||
for child in self._box.get_layout_children():
|
||||
rect = child.grid_rect
|
||||
if child.grid_rect:
|
||||
child.allocate(rect.x * _CELL_SIZE,
|
||||
rect.y * _CELL_SIZE,
|
||||
rect.width * _CELL_SIZE,
|
||||
rect.height * _CELL_SIZE,
|
||||
origin_changed)
|
||||
else:
|
||||
min_w, child_width = child.get_width_request()
|
||||
min_h, child_height = child.get_height_request(child_width)
|
||||
child.allocate(x + (width - child_width) / 2,
|
||||
y + (height - child_height) / 2,
|
||||
child_width, child_height, origin_changed)
|
||||
|
||||
def _get_child_grid_size(self, child):
|
||||
min_width, width = child.get_width_request()
|
||||
min_height, height = child.get_height_request(width)
|
||||
|
||||
return int(width / _CELL_SIZE), int(height / _CELL_SIZE)
|
||||
|
||||
def _grid_child_changed_cb(self, grid, box_child):
|
||||
box_child.item.emit_request_changed()
|
||||
@@ -0,0 +1,147 @@
|
||||
# Copyright (C) 2007, Red Hat, Inc.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""
|
||||
All the constants are expressed in pixels. They are defined for the XO screen
|
||||
and are usually adapted to different resolution by applying a zoom factor. The
|
||||
factor for traditional 96 dpi screen is currently 0.72 which is the inverse
|
||||
of the one we are using to adapt web pages to the XO screen. It should be
|
||||
considered a reference value rather then a scale constant which has to be
|
||||
automatically applied and always respected.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
import gtk
|
||||
import pango
|
||||
|
||||
_XO_DPI = 200.0
|
||||
|
||||
_FOCUS_LINE_WIDTH = 2
|
||||
_TAB_CURVATURE = 1
|
||||
|
||||
def _get_screen_dpi():
|
||||
xft_dpi = gtk.settings_get_default().get_property('gtk-xft-dpi')
|
||||
return float(xft_dpi / 1024)
|
||||
|
||||
def _compute_zoom_factor():
|
||||
if _get_screen_dpi() == 96.0:
|
||||
if not os.environ.has_key('SUGAR_XO_STYLE') or \
|
||||
not os.environ['SUGAR_XO_STYLE'] == 'yes':
|
||||
return 0.72
|
||||
|
||||
return 1.0
|
||||
|
||||
def _compute_font_height(font):
|
||||
widget = gtk.Label('')
|
||||
|
||||
context = widget.get_pango_context()
|
||||
pango_font = context.load_font(font.get_pango_desc())
|
||||
metrics = pango_font.get_metrics()
|
||||
|
||||
return pango.PIXELS(metrics.get_ascent() + metrics.get_descent())
|
||||
|
||||
class Font(object):
|
||||
def __init__(self, desc):
|
||||
self._desc = desc
|
||||
|
||||
def __str__(self):
|
||||
return self._desc
|
||||
|
||||
def get_pango_desc(self):
|
||||
return pango.FontDescription(self._desc)
|
||||
|
||||
class Color(object):
|
||||
def __init__(self, color, alpha=1.0):
|
||||
self._r, self._g, self._b = self._html_to_rgb(color)
|
||||
self._a = alpha
|
||||
|
||||
def get_rgba(self):
|
||||
return (self._r, self._g, self._b, self._a)
|
||||
|
||||
def get_int(self):
|
||||
return int(self._a * 255) + (int(self._b * 255) << 8) + \
|
||||
(int(self._g * 255) << 16) + (int(self._r * 255) << 24)
|
||||
|
||||
def get_gdk_color(self):
|
||||
return gtk.gdk.Color(int(self._r * 65535), int(self._g * 65535),
|
||||
int(self._b * 65535))
|
||||
|
||||
def get_html(self):
|
||||
return '#%02x%02x%02x' % (self._r * 255, self._g * 255, self._b * 255)
|
||||
|
||||
def _html_to_rgb(self, html_color):
|
||||
""" #RRGGBB -> (r, g, b) tuple (in float format) """
|
||||
|
||||
html_color = html_color.strip()
|
||||
if html_color[0] == '#':
|
||||
html_color = html_color[1:]
|
||||
if len(html_color) != 6:
|
||||
raise ValueError, "input #%s is not in #RRGGBB format" % html_color
|
||||
|
||||
r, g, b = html_color[:2], html_color[2:4], html_color[4:]
|
||||
r, g, b = [int(n, 16) for n in (r, g, b)]
|
||||
r, g, b = (r / 255.0, g / 255.0, b / 255.0)
|
||||
|
||||
return (r, g, b)
|
||||
|
||||
def get_svg(self):
|
||||
if self._a == 0.0:
|
||||
return 'none'
|
||||
else:
|
||||
return self.get_html()
|
||||
|
||||
def zoom(units):
|
||||
return int(ZOOM_FACTOR * units)
|
||||
|
||||
ZOOM_FACTOR = _compute_zoom_factor()
|
||||
|
||||
DEFAULT_SPACING = zoom(8)
|
||||
DEFAULT_PADDING = zoom(6)
|
||||
GRID_CELL_SIZE = zoom(75)
|
||||
LINE_WIDTH = zoom(2)
|
||||
|
||||
STANDARD_ICON_SIZE = zoom(55)
|
||||
SMALL_ICON_SIZE = zoom(55 * 0.5)
|
||||
MEDIUM_ICON_SIZE = zoom(55 * 1.5)
|
||||
LARGE_ICON_SIZE = zoom(55 * 2.0)
|
||||
XLARGE_ICON_SIZE = zoom(55 * 2.75)
|
||||
|
||||
FONT_SIZE = zoom(7 * _XO_DPI / _get_screen_dpi())
|
||||
FONT_NORMAL = Font('Bitstream Vera Sans %d' % FONT_SIZE)
|
||||
FONT_BOLD = Font('Bitstream Vera Sans bold %d' % FONT_SIZE)
|
||||
FONT_NORMAL_H = _compute_font_height(FONT_NORMAL)
|
||||
FONT_BOLD_H = _compute_font_height(FONT_BOLD)
|
||||
|
||||
TOOLBOX_SEPARATOR_HEIGHT = zoom(9)
|
||||
TOOLBOX_HORIZONTAL_PADDING = zoom(75)
|
||||
TOOLBOX_TAB_VBORDER = int((zoom(36) - FONT_NORMAL_H - _FOCUS_LINE_WIDTH) / 2)
|
||||
TOOLBOX_TAB_HBORDER = zoom(15) - _FOCUS_LINE_WIDTH - _TAB_CURVATURE
|
||||
TOOLBOX_TAB_LABEL_WIDTH = zoom(150 - 15 * 2)
|
||||
|
||||
COLOR_BLACK = Color('#000000')
|
||||
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')
|
||||
COLOR_TEXT_FIELD_GREY = Color('#E5E5E5')
|
||||
|
||||
PALETTE_CURSOR_DISTANCE = zoom(10)
|
||||
@@ -0,0 +1,63 @@
|
||||
# Copyright (C) 2007, Red Hat, Inc.
|
||||
#
|
||||
# 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.graphics.icon import Icon
|
||||
from sugar.graphics.palette import Palette, ToolInvoker
|
||||
|
||||
class ToggleToolButton(gtk.ToggleToolButton):
|
||||
__gtype_name__ = "SugarToggleToolButton"
|
||||
|
||||
def __init__(self, named_icon=None):
|
||||
gtk.ToggleToolButton.__init__(self)
|
||||
self._palette = None
|
||||
self.set_named_icon(named_icon)
|
||||
|
||||
def set_named_icon(self, named_icon):
|
||||
icon = Icon(icon_name=named_icon)
|
||||
self.set_icon_widget(icon)
|
||||
icon.show()
|
||||
|
||||
def get_palette(self):
|
||||
return self._palette
|
||||
|
||||
def set_palette(self, palette):
|
||||
if self._palette is not None:
|
||||
self._palette.props.invoker = None
|
||||
self._palette = palette
|
||||
self._palette.props.invoker = ToolInvoker(self)
|
||||
|
||||
def set_tooltip(self, text):
|
||||
self.set_palette(Palette(text))
|
||||
|
||||
def do_expose_event(self, event):
|
||||
if self._palette and self._palette.is_up():
|
||||
invoker = self._palette.props.invoker
|
||||
invoker.draw_rectangle(event, self._palette)
|
||||
elif self.child.state == gtk.STATE_PRELIGHT:
|
||||
self.child.style.paint_box(event.window, gtk.STATE_PRELIGHT,
|
||||
gtk.SHADOW_NONE, event.area,
|
||||
self.child, "toolbutton-prelight",
|
||||
self.allocation.x,
|
||||
self.allocation.y,
|
||||
self.allocation.width,
|
||||
self.allocation.height)
|
||||
|
||||
gtk.ToggleToolButton.do_expose_event(self, event)
|
||||
|
||||
palette = property(get_palette, set_palette)
|
||||
@@ -0,0 +1,91 @@
|
||||
# Copyright (C) 2007, Red Hat, Inc.
|
||||
#
|
||||
# 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
|
||||
import gobject
|
||||
import hippo
|
||||
|
||||
from sugar.graphics.toolbutton import ToolButton
|
||||
from sugar.graphics import style
|
||||
|
||||
class Toolbox(gtk.VBox):
|
||||
__gtype_name__ = 'SugarToolbox'
|
||||
|
||||
__gsignals__ = {
|
||||
'current-toolbar-changed': (gobject.SIGNAL_RUN_FIRST,
|
||||
gobject.TYPE_NONE,
|
||||
([int]))
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
gtk.VBox.__init__(self)
|
||||
|
||||
self._notebook = gtk.Notebook()
|
||||
self._notebook.set_tab_pos(gtk.POS_BOTTOM)
|
||||
self._notebook.set_show_border(False)
|
||||
self._notebook.set_show_tabs(False)
|
||||
self._notebook.props.tab_vborder = style.TOOLBOX_TAB_VBORDER
|
||||
self._notebook.props.tab_hborder = style.TOOLBOX_TAB_HBORDER
|
||||
self.pack_start(self._notebook)
|
||||
self._notebook.show()
|
||||
|
||||
# FIXME improve gtk.Notebook and do this in the theme
|
||||
self._separator = hippo.Canvas()
|
||||
box = hippo.CanvasBox(
|
||||
border_color=style.COLOR_BUTTON_GREY.get_int(),
|
||||
background_color=style.COLOR_PANEL_GREY.get_int(),
|
||||
box_height=style.TOOLBOX_SEPARATOR_HEIGHT,
|
||||
border_bottom=style.LINE_WIDTH)
|
||||
self._separator.set_root(box)
|
||||
self.pack_start(self._separator, False)
|
||||
|
||||
self._notebook.connect('notify::page', self._notify_page_cb)
|
||||
|
||||
def _notify_page_cb(self, notebook, pspec):
|
||||
self.emit('current-toolbar-changed', notebook.props.page)
|
||||
|
||||
def add_toolbar(self, name, toolbar):
|
||||
label = gtk.Label(name)
|
||||
label.set_size_request(style.TOOLBOX_TAB_LABEL_WIDTH, -1)
|
||||
label.set_alignment(0.0, 0.5)
|
||||
|
||||
event_box = gtk.EventBox()
|
||||
|
||||
alignment = gtk.Alignment(0.0, 0.0, 1.0, 1.0)
|
||||
alignment.set_padding(0, 0, style.TOOLBOX_HORIZONTAL_PADDING,
|
||||
style.TOOLBOX_HORIZONTAL_PADDING)
|
||||
|
||||
alignment.add(toolbar)
|
||||
event_box.add(alignment)
|
||||
alignment.show()
|
||||
event_box.show()
|
||||
|
||||
self._notebook.append_page(event_box, label)
|
||||
|
||||
if self._notebook.get_n_pages() > 1:
|
||||
self._notebook.set_show_tabs(True)
|
||||
self._separator.show()
|
||||
|
||||
def remove_toolbar(self, index):
|
||||
self._notebook.remove_page(index)
|
||||
|
||||
if self._notebook.get_n_pages() < 2:
|
||||
self._notebook.set_show_tabs(False)
|
||||
self._separator.hide()
|
||||
|
||||
def set_current_toolbar(self, index):
|
||||
self._notebook.set_current_page(index)
|
||||
@@ -0,0 +1,71 @@
|
||||
# Copyright (C) 2007, Red Hat, Inc.
|
||||
#
|
||||
# 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
|
||||
import gobject
|
||||
import time
|
||||
|
||||
from sugar.graphics.icon import Icon
|
||||
from sugar.graphics.palette import Palette, ToolInvoker
|
||||
|
||||
class ToolButton(gtk.ToolButton):
|
||||
__gtype_name__ = "SugarToolButton"
|
||||
|
||||
def __init__(self, icon_name=None):
|
||||
gtk.ToolButton.__init__(self)
|
||||
self._palette = None
|
||||
if icon_name:
|
||||
self.set_icon(icon_name)
|
||||
self.connect('clicked', self._button_clicked_cb)
|
||||
|
||||
def set_icon(self, icon_name):
|
||||
icon = Icon(icon_name=icon_name)
|
||||
self.set_icon_widget(icon)
|
||||
icon.show()
|
||||
|
||||
def get_palette(self):
|
||||
return self._palette
|
||||
|
||||
def set_palette(self, palette):
|
||||
if self._palette is not None:
|
||||
self._palette.props.invoker = None
|
||||
self._palette = palette
|
||||
self._palette.props.invoker = ToolInvoker(self)
|
||||
|
||||
def set_tooltip(self, text):
|
||||
self.set_palette(Palette(text))
|
||||
|
||||
def do_expose_event(self, event):
|
||||
if self._palette and self._palette.is_up():
|
||||
invoker = self._palette.props.invoker
|
||||
invoker.draw_rectangle(event, self._palette)
|
||||
elif self.child.state == gtk.STATE_PRELIGHT:
|
||||
self.child.style.paint_box(event.window, gtk.STATE_PRELIGHT,
|
||||
gtk.SHADOW_NONE, event.area,
|
||||
self.child, "toolbutton-prelight",
|
||||
self.allocation.x,
|
||||
self.allocation.y,
|
||||
self.allocation.width,
|
||||
self.allocation.height)
|
||||
|
||||
gtk.ToolButton.do_expose_event(self, event)
|
||||
|
||||
def _button_clicked_cb(self, widget):
|
||||
if self._palette:
|
||||
self._palette.popdown(True)
|
||||
|
||||
palette = property(get_palette, set_palette)
|
||||
@@ -0,0 +1,59 @@
|
||||
# Copyright (C) 2007, Red Hat, Inc.
|
||||
#
|
||||
# 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
|
||||
import gobject
|
||||
|
||||
from sugar.graphics.combobox import ComboBox
|
||||
from sugar.graphics import style
|
||||
|
||||
class ToolComboBox(gtk.ToolItem):
|
||||
__gproperties__ = {
|
||||
'label-text' : (str, None, None, None,
|
||||
gobject.PARAM_WRITABLE),
|
||||
}
|
||||
|
||||
def __init__(self, combo=None, **kwargs):
|
||||
self.label = None
|
||||
self._label_text = ''
|
||||
|
||||
gobject.GObject.__init__(self, **kwargs)
|
||||
|
||||
self.set_border_width(style.DEFAULT_PADDING)
|
||||
|
||||
hbox = gtk.HBox(False, style.DEFAULT_SPACING)
|
||||
|
||||
self.label = gtk.Label(self._label_text)
|
||||
hbox.pack_start(self.label, False)
|
||||
self.label.show()
|
||||
|
||||
if combo:
|
||||
self.combo = combo
|
||||
else:
|
||||
self.combo = ComboBox()
|
||||
|
||||
hbox.pack_start(self.combo)
|
||||
self.combo.show()
|
||||
|
||||
self.add(hbox)
|
||||
hbox.show()
|
||||
|
||||
def do_set_property(self, pspec, value):
|
||||
if pspec.name == 'label-text':
|
||||
self._label_text = value
|
||||
if self.label:
|
||||
self.label.set_text(self._label_text)
|
||||
@@ -0,0 +1,241 @@
|
||||
# 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 gobject
|
||||
import gtk
|
||||
|
||||
from sugar.graphics import style
|
||||
from sugar.graphics.palette import Palette, ToolInvoker
|
||||
from sugar.graphics.toolbutton import ToolButton
|
||||
from sugar.graphics.icon import Icon
|
||||
|
||||
_PREVIOUS_PAGE = 0
|
||||
_NEXT_PAGE = 1
|
||||
|
||||
class _TrayViewport(gtk.Viewport):
|
||||
__gproperties__ = {
|
||||
'can-scroll' : (bool, None, None, False,
|
||||
gobject.PARAM_READABLE),
|
||||
}
|
||||
|
||||
def __init__(self, orientation):
|
||||
self.orientation = orientation
|
||||
self._can_scroll = False
|
||||
|
||||
gobject.GObject.__init__(self)
|
||||
|
||||
self.set_shadow_type(gtk.SHADOW_NONE)
|
||||
|
||||
self.traybar = gtk.Toolbar()
|
||||
self.traybar.set_orientation(orientation)
|
||||
self.traybar.set_show_arrow(False)
|
||||
self.add(self.traybar)
|
||||
self.traybar.show()
|
||||
|
||||
self.connect('size_allocate', self._size_allocate_cb)
|
||||
|
||||
def scroll(self, direction):
|
||||
if direction == _PREVIOUS_PAGE:
|
||||
self._scroll_previous()
|
||||
elif direction == _NEXT_PAGE:
|
||||
self._scroll_next()
|
||||
|
||||
def _scroll_next(self):
|
||||
if self.orientation == gtk.ORIENTATION_HORIZONTAL:
|
||||
adj = self.get_hadjustment()
|
||||
new_value = adj.value + self.allocation.width
|
||||
adj.value = min(new_value, adj.upper - self.allocation.width)
|
||||
else:
|
||||
adj = self.get_vadjustment()
|
||||
new_value = adj.value + self.allocation.height
|
||||
adj.value = min(new_value, adj.upper - self.allocation.height)
|
||||
|
||||
def _scroll_previous(self):
|
||||
if self.orientation == gtk.ORIENTATION_HORIZONTAL:
|
||||
adj = self.get_hadjustment()
|
||||
new_value = adj.value - self.allocation.width
|
||||
adj.value = max(adj.lower, new_value)
|
||||
else:
|
||||
adj = self.get_vadjustment()
|
||||
new_value = adj.value - self.allocation.height
|
||||
adj.value = max(adj.lower, new_value)
|
||||
|
||||
def do_size_request(self, requisition):
|
||||
child_requisition = self.child.size_request()
|
||||
if self.orientation == gtk.ORIENTATION_HORIZONTAL:
|
||||
requisition[0] = 0
|
||||
requisition[1] = child_requisition[1]
|
||||
else:
|
||||
requisition[0] = child_requisition[0]
|
||||
requisition[1] = 0
|
||||
|
||||
def do_get_property(self, pspec):
|
||||
if pspec.name == 'can-scroll':
|
||||
return self._can_scroll
|
||||
|
||||
def _size_allocate_cb(self, viewport, allocation):
|
||||
bar_requisition = self.traybar.get_child_requisition()
|
||||
if self.orientation == gtk.ORIENTATION_HORIZONTAL:
|
||||
can_scroll = bar_requisition[0] > allocation.width
|
||||
else:
|
||||
can_scroll = bar_requisition[1] > allocation.height
|
||||
|
||||
if can_scroll != self._can_scroll:
|
||||
self._can_scroll = can_scroll
|
||||
self.notify('can-scroll')
|
||||
|
||||
class _TrayScrollButton(gtk.Button):
|
||||
def __init__(self, icon_name, scroll_direction):
|
||||
gobject.GObject.__init__(self)
|
||||
|
||||
self._viewport = None
|
||||
|
||||
self._scroll_direction = scroll_direction
|
||||
|
||||
self.set_relief(gtk.RELIEF_NONE)
|
||||
self.set_size_request(style.GRID_CELL_SIZE, style.GRID_CELL_SIZE)
|
||||
|
||||
icon = Icon(icon_name = icon_name,
|
||||
icon_size=gtk.ICON_SIZE_SMALL_TOOLBAR)
|
||||
self.set_image(icon)
|
||||
icon.show()
|
||||
|
||||
self.connect('clicked', self._clicked_cb)
|
||||
|
||||
def set_viewport(self, viewport):
|
||||
self._viewport = viewport
|
||||
self._viewport.connect('notify::can-scroll',
|
||||
self._viewport_can_scroll_changed_cb)
|
||||
|
||||
def _viewport_can_scroll_changed_cb(self, viewport, pspec):
|
||||
self.props.visible = self._viewport.props.can_scroll
|
||||
|
||||
def _clicked_cb(self, button):
|
||||
self._viewport.scroll(self._scroll_direction)
|
||||
|
||||
viewport = property(fset=set_viewport)
|
||||
|
||||
class HTray(gtk.HBox):
|
||||
def __init__(self, **kwargs):
|
||||
gobject.GObject.__init__(self, **kwargs)
|
||||
|
||||
scroll_left = _TrayScrollButton('go-left', _PREVIOUS_PAGE)
|
||||
self.pack_start(scroll_left, False)
|
||||
|
||||
self._viewport = _TrayViewport(gtk.ORIENTATION_HORIZONTAL)
|
||||
self.pack_start(self._viewport)
|
||||
self._viewport.show()
|
||||
|
||||
scroll_right = _TrayScrollButton('go-right', _NEXT_PAGE)
|
||||
self.pack_start(scroll_right, False)
|
||||
|
||||
scroll_left.viewport = self._viewport
|
||||
scroll_right.viewport = self._viewport
|
||||
|
||||
def get_children(self):
|
||||
return self._viewport.traybar.get_children()
|
||||
|
||||
def add_item(self, item, index=-1):
|
||||
self._viewport.traybar.insert(item, index)
|
||||
|
||||
def remove_item(self, item):
|
||||
self._viewport.traybar.remove(item)
|
||||
|
||||
def get_item_index(self, item):
|
||||
return self._viewport.traybar.get_item_index(item)
|
||||
|
||||
class VTray(gtk.VBox):
|
||||
def __init__(self, **kwargs):
|
||||
gobject.GObject.__init__(self, **kwargs)
|
||||
|
||||
# FIXME we need a go-up icon
|
||||
scroll_left = _TrayScrollButton('go-left', _PREVIOUS_PAGE)
|
||||
self.pack_start(scroll_left, False)
|
||||
|
||||
self._viewport = _TrayViewport(gtk.ORIENTATION_VERTICAL)
|
||||
self.pack_start(self._viewport)
|
||||
self._viewport.show()
|
||||
|
||||
# FIXME we need a go-down icon
|
||||
scroll_right = _TrayScrollButton('go-right', _NEXT_PAGE)
|
||||
self.pack_start(scroll_right, False)
|
||||
|
||||
scroll_left.viewport = self._viewport
|
||||
scroll_right.viewport = self._viewport
|
||||
|
||||
def get_children(self):
|
||||
return self._viewport.traybar.get_children()
|
||||
|
||||
def add_item(self, item, index=-1):
|
||||
self._viewport.traybar.insert(item, index)
|
||||
|
||||
def remove_item(self, item):
|
||||
self._viewport.traybar.remove(item)
|
||||
|
||||
def get_item_index(self, item):
|
||||
return self._viewport.traybar.get_item_index(item)
|
||||
|
||||
class TrayButton(ToolButton):
|
||||
def __init__(self, **kwargs):
|
||||
ToolButton.__init__(self, **kwargs)
|
||||
|
||||
class _IconWidget(gtk.EventBox):
|
||||
__gtype_name__ = "SugarTrayIconWidget"
|
||||
|
||||
def __init__(self, icon_name=None, xo_color=None):
|
||||
gtk.EventBox.__init__(self)
|
||||
|
||||
self._palette = None
|
||||
|
||||
self.set_app_paintable(True)
|
||||
|
||||
icon = Icon(icon_name=icon_name, xo_color=xo_color,
|
||||
icon_size=gtk.ICON_SIZE_LARGE_TOOLBAR)
|
||||
self.add(icon)
|
||||
icon.show()
|
||||
|
||||
def do_expose_event(self, event):
|
||||
if self._palette and self._palette.is_up():
|
||||
invoker = self._palette.props.invoker
|
||||
invoker.draw_rectangle(event, self._palette)
|
||||
|
||||
gtk.EventBox.do_expose_event(self, event)
|
||||
|
||||
def set_palette(self, palette):
|
||||
if self._palette is not None:
|
||||
self._palette.props.invoker = None
|
||||
self._palette = palette
|
||||
self._palette.props.invoker = ToolInvoker(self)
|
||||
|
||||
class TrayIcon(gtk.ToolItem):
|
||||
__gtype_name__ = "SugarTrayIcon"
|
||||
|
||||
def __init__(self, icon_name=None, xo_color=None):
|
||||
gtk.ToolItem.__init__(self)
|
||||
|
||||
self._icon_widget = _IconWidget(icon_name, xo_color)
|
||||
self.add(self._icon_widget)
|
||||
self._icon_widget.show()
|
||||
|
||||
self.set_size_request(style.GRID_CELL_SIZE, style.GRID_CELL_SIZE)
|
||||
|
||||
def set_palette(self, palette):
|
||||
self._icon_widget.set_palette(palette)
|
||||
|
||||
def set_tooltip(self, text):
|
||||
self.set_palette(Palette(text))
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
# Copyright (C) 2007, Red Hat, Inc.
|
||||
#
|
||||
# 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
|
||||
import hippo
|
||||
|
||||
class Window(gtk.Window):
|
||||
def __init__(self):
|
||||
gtk.Window.__init__(self)
|
||||
|
||||
self.connect('realize', self._window_realize_cb)
|
||||
|
||||
self.toolbox = None
|
||||
self._alerts = []
|
||||
self.alert_position = -1
|
||||
self.canvas = None
|
||||
|
||||
self._vbox = gtk.VBox()
|
||||
self.add(self._vbox)
|
||||
self._vbox.show()
|
||||
|
||||
def set_canvas(self, canvas):
|
||||
if self.canvas:
|
||||
self._vbox.remove(self.canvas)
|
||||
|
||||
self._vbox.pack_start(canvas)
|
||||
self._vbox.reorder_child(canvas, -1)
|
||||
|
||||
self.canvas = canvas
|
||||
|
||||
def set_toolbox(self, toolbox):
|
||||
if self.toolbox:
|
||||
self._vbox.remove(self.toolbox)
|
||||
|
||||
self._vbox.pack_start(toolbox, False)
|
||||
self._vbox.reorder_child(toolbox, 0)
|
||||
|
||||
self.toolbox = toolbox
|
||||
|
||||
def add_alert(self, alert):
|
||||
self._alerts.append(alert)
|
||||
if len(self._alerts) == 1:
|
||||
self._vbox.pack_start(alert, False)
|
||||
self._vbox.reorder_child(alert, self.alert_position)
|
||||
|
||||
def remove_alert(self, alert):
|
||||
if alert in self._alerts:
|
||||
self._alerts.remove(alert)
|
||||
self._vbox.remove(alert)
|
||||
if len(self._alerts) >= 1:
|
||||
self._vbox.pack_start(self._alerts[0], False)
|
||||
self._vbox.reorder_child(self._alerts[0], self.alert_position)
|
||||
|
||||
def _window_realize_cb(self, window):
|
||||
group = gtk.Window()
|
||||
group.realize()
|
||||
window.window.set_group(group.window)
|
||||
|
||||
def get_canvas_screenshot(self):
|
||||
if not self.canvas:
|
||||
return None
|
||||
|
||||
window = self.canvas.window
|
||||
width, height = window.get_size()
|
||||
|
||||
screenshot = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, has_alpha=False,
|
||||
bits_per_sample=8, width=width, height=height)
|
||||
screenshot.get_from_drawable(window, window.get_colormap(), 0, 0, 0, 0,
|
||||
width, height)
|
||||
return screenshot
|
||||
@@ -0,0 +1,255 @@
|
||||
# Copyright (C) 2006-2007 Red Hat, Inc.
|
||||
#
|
||||
# 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 random
|
||||
|
||||
_colors = [
|
||||
['#B20008', '#FF2B34'], \
|
||||
['#FF2B34', '#B20008'], \
|
||||
['#E6000A', '#FF2B34'], \
|
||||
['#FF2B34', '#E6000A'], \
|
||||
['#FFADCE', '#FF2B34'], \
|
||||
['#9A5200', '#FF2B34'], \
|
||||
['#FF2B34', '#9A5200'], \
|
||||
['#FF8F00', '#FF2B34'], \
|
||||
['#FF2B34', '#FF8F00'], \
|
||||
['#FFC169', '#FF2B34'], \
|
||||
['#807500', '#FF2B34'], \
|
||||
['#FF2B34', '#807500'], \
|
||||
['#BE9E00', '#FF2B34'], \
|
||||
['#FF2B34', '#BE9E00'], \
|
||||
['#F8E800', '#FF2B34'], \
|
||||
['#008009', '#FF2B34'], \
|
||||
['#FF2B34', '#008009'], \
|
||||
['#00B20D', '#FF2B34'], \
|
||||
['#FF2B34', '#00B20D'], \
|
||||
['#8BFF7A', '#FF2B34'], \
|
||||
['#00588C', '#FF2B34'], \
|
||||
['#FF2B34', '#00588C'], \
|
||||
['#005FE4', '#FF2B34'], \
|
||||
['#FF2B34', '#005FE4'], \
|
||||
['#BCCDFF', '#FF2B34'], \
|
||||
['#5E008C', '#FF2B34'], \
|
||||
['#FF2B34', '#5E008C'], \
|
||||
['#7F00BF', '#FF2B34'], \
|
||||
['#FF2B34', '#7F00BF'], \
|
||||
['#D1A3FF', '#FF2B34'], \
|
||||
['#9A5200', '#FF8F00'], \
|
||||
['#FF8F00', '#9A5200'], \
|
||||
['#C97E00', '#FF8F00'], \
|
||||
['#FF8F00', '#C97E00'], \
|
||||
['#FFC169', '#FF8F00'], \
|
||||
['#807500', '#FF8F00'], \
|
||||
['#FF8F00', '#807500'], \
|
||||
['#BE9E00', '#FF8F00'], \
|
||||
['#FF8F00', '#BE9E00'], \
|
||||
['#F8E800', '#FF8F00'], \
|
||||
['#008009', '#FF8F00'], \
|
||||
['#FF8F00', '#008009'], \
|
||||
['#00B20D', '#FF8F00'], \
|
||||
['#FF8F00', '#00B20D'], \
|
||||
['#8BFF7A', '#FF8F00'], \
|
||||
['#00588C', '#FF8F00'], \
|
||||
['#FF8F00', '#00588C'], \
|
||||
['#005FE4', '#FF8F00'], \
|
||||
['#FF8F00', '#005FE4'], \
|
||||
['#BCCDFF', '#FF8F00'], \
|
||||
['#5E008C', '#FF8F00'], \
|
||||
['#FF8F00', '#5E008C'], \
|
||||
['#A700FF', '#FF8F00'], \
|
||||
['#FF8F00', '#A700FF'], \
|
||||
['#D1A3FF', '#FF8F00'], \
|
||||
['#B20008', '#FF8F00'], \
|
||||
['#FF8F00', '#B20008'], \
|
||||
['#FF2B34', '#FF8F00'], \
|
||||
['#FF8F00', '#FF2B34'], \
|
||||
['#FFADCE', '#FF8F00'], \
|
||||
['#807500', '#F8E800'], \
|
||||
['#F8E800', '#807500'], \
|
||||
['#BE9E00', '#F8E800'], \
|
||||
['#F8E800', '#BE9E00'], \
|
||||
['#FFFA00', '#EDDE00'], \
|
||||
['#008009', '#F8E800'], \
|
||||
['#F8E800', '#008009'], \
|
||||
['#00EA11', '#F8E800'], \
|
||||
['#F8E800', '#00EA11'], \
|
||||
['#8BFF7A', '#F8E800'], \
|
||||
['#00588C', '#F8E800'], \
|
||||
['#F8E800', '#00588C'], \
|
||||
['#00A0FF', '#F8E800'], \
|
||||
['#F8E800', '#00A0FF'], \
|
||||
['#BCCEFF', '#F8E800'], \
|
||||
['#5E008C', '#F8E800'], \
|
||||
['#F8E800', '#5E008C'], \
|
||||
['#AC32FF', '#F8E800'], \
|
||||
['#F8E800', '#AC32FF'], \
|
||||
['#D1A3FF', '#F8E800'], \
|
||||
['#B20008', '#F8E800'], \
|
||||
['#F8E800', '#B20008'], \
|
||||
['#FF2B34', '#F8E800'], \
|
||||
['#F8E800', '#FF2B34'], \
|
||||
['#FFADCE', '#F8E800'], \
|
||||
['#9A5200', '#F8E800'], \
|
||||
['#F8E800', '#9A5200'], \
|
||||
['#FF8F00', '#F8E800'], \
|
||||
['#F8E800', '#FF8F00'], \
|
||||
['#FFC169', '#F8E800'], \
|
||||
['#008009', '#00EA11'], \
|
||||
['#00EA11', '#008009'], \
|
||||
['#00B20D', '#00EA11'], \
|
||||
['#00EA11', '#00B20D'], \
|
||||
['#8BFF7A', '#00EA11'], \
|
||||
['#00588C', '#00EA11'], \
|
||||
['#00EA11', '#00588C'], \
|
||||
['#005FE4', '#00EA11'], \
|
||||
['#00EA11', '#005FE4'], \
|
||||
['#BCCDFF', '#00EA11'], \
|
||||
['#5E008C', '#00EA11'], \
|
||||
['#00EA11', '#5E008C'], \
|
||||
['#7F00BF', '#00EA11'], \
|
||||
['#00EA11', '#7F00BF'], \
|
||||
['#D1A3FF', '#00EA11'], \
|
||||
['#B20008', '#00EA11'], \
|
||||
['#00EA11', '#B20008'], \
|
||||
['#FF2B34', '#00EA11'], \
|
||||
['#00EA11', '#FF2B34'], \
|
||||
['#FFADCE', '#00EA11'], \
|
||||
['#9A5200', '#00EA11'], \
|
||||
['#00EA11', '#9A5200'], \
|
||||
['#FF8F00', '#00EA11'], \
|
||||
['#00EA11', '#FF8F00'], \
|
||||
['#FFC169', '#00EA11'], \
|
||||
['#807500', '#00EA11'], \
|
||||
['#00EA11', '#807500'], \
|
||||
['#BE9E00', '#00EA11'], \
|
||||
['#00EA11', '#BE9E00'], \
|
||||
['#F8E800', '#00EA11'], \
|
||||
['#00588C', '#00A0FF'], \
|
||||
['#00A0FF', '#00588C'], \
|
||||
['#005FE4', '#00A0FF'], \
|
||||
['#00A0FF', '#005FE4'], \
|
||||
['#BCCDFF', '#00A0FF'], \
|
||||
['#5E008C', '#00A0FF'], \
|
||||
['#00A0FF', '#5E008C'], \
|
||||
['#9900E6', '#00A0FF'], \
|
||||
['#00A0FF', '#9900E6'], \
|
||||
['#D1A3FF', '#00A0FF'], \
|
||||
['#B20008', '#00A0FF'], \
|
||||
['#00A0FF', '#B20008'], \
|
||||
['#FF2B34', '#00A0FF'], \
|
||||
['#00A0FF', '#FF2B34'], \
|
||||
['#FFADCE', '#00A0FF'], \
|
||||
['#9A5200', '#00A0FF'], \
|
||||
['#00A0FF', '#9A5200'], \
|
||||
['#FF8F00', '#00A0FF'], \
|
||||
['#00A0FF', '#FF8F00'], \
|
||||
['#FFC169', '#00A0FF'], \
|
||||
['#807500', '#00A0FF'], \
|
||||
['#00A0FF', '#807500'], \
|
||||
['#BE9E00', '#00A0FF'], \
|
||||
['#00A0FF', '#BE9E00'], \
|
||||
['#F8E800', '#00A0FF'], \
|
||||
['#008009', '#00A0FF'], \
|
||||
['#00A0FF', '#008009'], \
|
||||
['#00B20D', '#00A0FF'], \
|
||||
['#00A0FF', '#00B20D'], \
|
||||
['#8BFF7A', '#00A0FF'], \
|
||||
['#5E008C', '#AC32FF'], \
|
||||
['#AC32FF', '#5E008C'], \
|
||||
['#7F00BF', '#AC32FF'], \
|
||||
['#AC32FF', '#7F00BF'], \
|
||||
['#D1A3FF', '#AC32FF'], \
|
||||
['#B20008', '#AC32FF'], \
|
||||
['#AC32FF', '#B20008'], \
|
||||
['#FF2B34', '#AC32FF'], \
|
||||
['#AC32FF', '#FF2B34'], \
|
||||
['#FFADCE', '#AC32FF'], \
|
||||
['#9A5200', '#AC32FF'], \
|
||||
['#AC32FF', '#9A5200'], \
|
||||
['#FF8F00', '#AC32FF'], \
|
||||
['#AC32FF', '#FF8F00'], \
|
||||
['#FFC169', '#AC32FF'], \
|
||||
['#807500', '#AC32FF'], \
|
||||
['#AC32FF', '#807500'], \
|
||||
['#BE9E00', '#AC32FF'], \
|
||||
['#AC32FF', '#BE9E00'], \
|
||||
['#F8E800', '#AC32FF'], \
|
||||
['#008009', '#AC32FF'], \
|
||||
['#AC32FF', '#008009'], \
|
||||
['#00B20D', '#AC32FF'], \
|
||||
['#AC32FF', '#00B20D'], \
|
||||
['#8BFF7A', '#AC32FF'], \
|
||||
['#00588C', '#AC32FF'], \
|
||||
['#AC32FF', '#00588C'], \
|
||||
['#005FE4', '#AC32FF'], \
|
||||
['#AC32FF', '#005FE4'], \
|
||||
['#BCCDFF', '#AC32FF'], \
|
||||
]
|
||||
|
||||
def _parse_string(color_string):
|
||||
if color_string == 'white':
|
||||
return ['#ffffff', '#414141']
|
||||
elif color_string == 'insensitive':
|
||||
return ['#ffffff', '#e2e2e2']
|
||||
|
||||
splitted = color_string.split(',')
|
||||
if len(splitted) == 2:
|
||||
return [splitted[0], splitted[1]]
|
||||
else:
|
||||
return None
|
||||
|
||||
def is_valid(color_string):
|
||||
return (_parse_string(color_string) != None)
|
||||
|
||||
class XoColor:
|
||||
def __init__(self, color_string=None):
|
||||
if color_string == None or not is_valid(color_string):
|
||||
n = int(random.random() * (len(_colors) - 1))
|
||||
[self._stroke, self._fill] = _colors[n]
|
||||
else:
|
||||
[self._stroke, self._fill] = _parse_string(color_string)
|
||||
|
||||
def __cmp__(self, other):
|
||||
if isinstance(other, XoColor):
|
||||
if self._stroke == other._stroke and self._fill == other._fill:
|
||||
return 0
|
||||
return -1
|
||||
|
||||
def get_stroke_color(self):
|
||||
return self._stroke
|
||||
|
||||
def get_fill_color(self):
|
||||
return self._fill
|
||||
|
||||
def to_string(self):
|
||||
return '%s,%s' % (self._stroke, self._fill)
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
import re
|
||||
|
||||
f = open(sys.argv[1], 'r')
|
||||
|
||||
print '_colors = ['
|
||||
|
||||
for line in f.readlines():
|
||||
match = re.match(r'fill: ([A-Z0-9]*) stroke: ([A-Z0-9]*)', line)
|
||||
print "['#%s', '#%s'], \\" % (match.group(2), match.group(1))
|
||||
|
||||
print ']'
|
||||
|
||||
f.close()
|
||||
Reference in New Issue
Block a user