Rename the module to sugar3
The old gtk-2 based module will be present in the 0.94 branch in the sugar-toolkit. Signed-off-by: Simon Schampijer <simon@laptop.org> Acked-by: Sascha Silbe <silbe@activitycentral.com>
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
sugardir = $(pythondir)/sugar3/graphics
|
||||
sugar_PYTHON = \
|
||||
alert.py \
|
||||
animator.py \
|
||||
canvastextview.py \
|
||||
colorbutton.py \
|
||||
combobox.py \
|
||||
entry.py \
|
||||
iconentry.py \
|
||||
icon.py \
|
||||
__init__.py \
|
||||
menuitem.py \
|
||||
notebook.py \
|
||||
objectchooser.py \
|
||||
palettegroup.py \
|
||||
palette.py \
|
||||
palettewindow.py \
|
||||
panel.py \
|
||||
radiopalette.py \
|
||||
radiotoolbutton.py \
|
||||
roundbox.py \
|
||||
style.py \
|
||||
toggletoolbutton.py \
|
||||
toolbarbox.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,489 @@
|
||||
"""
|
||||
Alerts appear at the top of the body of your activity.
|
||||
|
||||
At a high level, Alert and its different variations (TimeoutAlert,
|
||||
ConfirmationAlert, etc.) have a title, an alert message and then several
|
||||
buttons that the user can click. The Alert class will pass "response" events
|
||||
to your activity when any of these buttons are clicked, along with a
|
||||
response_id to help you identify what button was clicked.
|
||||
|
||||
|
||||
Examples
|
||||
--------
|
||||
create a simple alert message.
|
||||
|
||||
.. code-block:: python
|
||||
from sugar.graphics.alert import Alert
|
||||
...
|
||||
# Create a new simple alert
|
||||
alert = Alert()
|
||||
# Populate the title and text body of the alert.
|
||||
alert.props.title=_('Title of Alert Goes Here')
|
||||
alert.props.msg = _('Text message of alert goes here')
|
||||
# Call the add_alert() method (inherited via the sugar.graphics.Window
|
||||
# superclass of Activity) to add this alert to the activity window.
|
||||
self.add_alert(alert)
|
||||
alert.show()
|
||||
|
||||
STABLE.
|
||||
"""
|
||||
# Copyright (C) 2007, One Laptop Per Child
|
||||
# Copyright (C) 2010, Anish Mangal <anishmangal2002@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 gettext
|
||||
|
||||
import gtk
|
||||
import gobject
|
||||
import pango
|
||||
import math
|
||||
|
||||
from sugar.graphics import style
|
||||
from sugar.graphics.icon import Icon
|
||||
|
||||
|
||||
_ = lambda msg: gettext.dgettext('sugar-toolkit', msg)
|
||||
|
||||
|
||||
class Alert(gtk.EventBox):
|
||||
"""
|
||||
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. The position of the alert is below the
|
||||
toolbox or top in fullscreen mode.
|
||||
|
||||
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):
|
||||
|
||||
self._title = None
|
||||
self._msg = None
|
||||
self._icon = None
|
||||
self._buttons = {}
|
||||
|
||||
self._hbox = gtk.HBox()
|
||||
self._hbox.set_border_width(style.DEFAULT_SPACING)
|
||||
self._hbox.set_spacing(style.DEFAULT_SPACING)
|
||||
|
||||
self._msg_box = gtk.VBox()
|
||||
self._title_label = gtk.Label()
|
||||
self._title_label.set_alignment(0, 0.5)
|
||||
self._msg_box.pack_start(self._title_label, False)
|
||||
|
||||
self._msg_label = gtk.Label()
|
||||
self._msg_label.set_alignment(0, 0.5)
|
||||
self._msg_box.pack_start(self._msg_label, False)
|
||||
self._hbox.pack_start(self._msg_box, False)
|
||||
|
||||
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)
|
||||
|
||||
gobject.GObject.__init__(self, **kwargs)
|
||||
|
||||
self.set_visible_window(True)
|
||||
self.add(self._hbox)
|
||||
self._title_label.show()
|
||||
self._msg_label.show()
|
||||
self._buttons_box.show()
|
||||
self._msg_box.show()
|
||||
self._hbox.show()
|
||||
self.show()
|
||||
|
||||
def do_set_property(self, pspec, value):
|
||||
"""
|
||||
Set alert property
|
||||
|
||||
Parameters
|
||||
----------
|
||||
pspec :
|
||||
|
||||
value :
|
||||
|
||||
Returns
|
||||
-------
|
||||
None
|
||||
|
||||
"""
|
||||
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)
|
||||
self._msg_label.set_line_wrap(True)
|
||||
elif pspec.name == 'icon':
|
||||
if self._icon != value:
|
||||
self._icon = value
|
||||
self._hbox.pack_start(self._icon, False)
|
||||
self._hbox.reorder_child(self._icon, 0)
|
||||
|
||||
def do_get_property(self, pspec):
|
||||
"""
|
||||
Get alert property
|
||||
|
||||
Parameters
|
||||
----------
|
||||
pspec :
|
||||
property for which the value will be returned
|
||||
|
||||
Returns
|
||||
-------
|
||||
value of the property specified
|
||||
|
||||
"""
|
||||
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
|
||||
|
||||
Parameters
|
||||
----------
|
||||
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
|
||||
|
||||
postion :
|
||||
the position of the button in the box (optional)
|
||||
|
||||
Returns
|
||||
-------
|
||||
button :gtk.Button
|
||||
|
||||
"""
|
||||
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 response id
|
||||
|
||||
Parameters
|
||||
----------
|
||||
response_id :
|
||||
|
||||
Returns
|
||||
-------
|
||||
None
|
||||
|
||||
"""
|
||||
self._buttons_box.remove(self._buttons[response_id])
|
||||
|
||||
def _response(self, response_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', 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.
|
||||
|
||||
A confirmation alert is a nice shortcut from a standard Alert because it
|
||||
comes with 'OK' and 'Cancel' buttons already built-in. When clicked, the
|
||||
'OK' button will emit a response with a response_id of gtk.RESPONSE_OK,
|
||||
while the 'Cancel' button will emit gtk.RESPONSE_CANCEL.
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
.. code-block:: python
|
||||
from sugar.graphics.alert import ConfirmationAlert
|
||||
...
|
||||
#### Method: _alert_confirmation, create a Confirmation alert (with ok
|
||||
and cancel buttons standard)
|
||||
# and add it to the UI.
|
||||
def _alert_confirmation(self):
|
||||
alert = ConfirmationAlert()
|
||||
alert.props.title=_('Title of Alert Goes Here')
|
||||
alert.props.msg = _('Text message of alert goes here')
|
||||
alert.connect('response', self._alert_response_cb)
|
||||
self.add_alert(alert)
|
||||
|
||||
|
||||
#### Method: _alert_response_cb, called when an alert object throws a
|
||||
response event.
|
||||
def _alert_response_cb(self, alert, response_id):
|
||||
#remove the alert from the screen, since either a response button
|
||||
#was clicked or there was a timeout
|
||||
self.remove_alert(alert)
|
||||
|
||||
#Do any work that is specific to the type of button clicked.
|
||||
if response_id is gtk.RESPONSE_OK:
|
||||
print 'Ok Button was clicked. Do any work upon ok here ...'
|
||||
elif response_id is gtk.RESPONSE_CANCEL:
|
||||
print 'Cancel Button was clicked.'
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
Alert.__init__(self, **kwargs)
|
||||
|
||||
icon = Icon(icon_name='dialog-cancel')
|
||||
self.add_button(gtk.RESPONSE_CANCEL, _('Cancel'), icon)
|
||||
icon.show()
|
||||
|
||||
icon = Icon(icon_name='dialog-ok')
|
||||
self.add_button(gtk.RESPONSE_OK, _('Ok'), icon)
|
||||
icon.show()
|
||||
|
||||
|
||||
class ErrorAlert(Alert):
|
||||
"""
|
||||
This is a ready-made one button (Ok) alert.
|
||||
|
||||
An error alert is a nice shortcut from a standard Alert because it
|
||||
comes with the 'OK' button already built-in. When clicked, the
|
||||
'OK' button will emit a response with a response_id of gtk.RESPONSE_OK.
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
.. code-block:: python
|
||||
from sugar.graphics.alert import ErrorAlert
|
||||
...
|
||||
#### Method: _alert_error, create a Error alert (with ok
|
||||
button standard)
|
||||
# and add it to the UI.
|
||||
def _alert_error(self):
|
||||
alert = ErrorAlert()
|
||||
alert.props.title=_('Title of Alert Goes Here')
|
||||
alert.props.msg = _('Text message of alert goes here')
|
||||
alert.connect('response', self._alert_response_cb)
|
||||
self.add_alert(alert)
|
||||
|
||||
|
||||
#### Method: _alert_response_cb, called when an alert object throws a
|
||||
response event.
|
||||
def _alert_response_cb(self, alert, response_id):
|
||||
#remove the alert from the screen, since either a response button
|
||||
#was clicked or there was a timeout
|
||||
self.remove_alert(alert)
|
||||
|
||||
#Do any work that is specific to the response_id.
|
||||
if response_id is gtk.RESPONSE_OK:
|
||||
print 'Ok Button was clicked. Do any work upon ok here ...'
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
Alert.__init__(self, **kwargs)
|
||||
|
||||
icon = Icon(icon_name='dialog-ok')
|
||||
self.add_button(gtk.RESPONSE_OK, _('Ok'), icon)
|
||||
icon.show()
|
||||
|
||||
|
||||
class _TimeoutIcon(gtk.Alignment):
|
||||
__gtype_name__ = 'SugarTimeoutIcon'
|
||||
|
||||
def __init__(self):
|
||||
gtk.Alignment.__init__(self, 0, 0, 1, 1)
|
||||
self.set_app_paintable(True)
|
||||
self._text = gtk.Label()
|
||||
self._text.set_alignment(0.5, 0.5)
|
||||
attrlist = pango.AttrList()
|
||||
attrlist.insert(pango.AttrWeight(pango.WEIGHT_BOLD))
|
||||
self._text.set_attributes(attrlist)
|
||||
self.add(self._text)
|
||||
self._text.show()
|
||||
self.connect("expose_event", self.__expose_cb)
|
||||
|
||||
def __expose_cb(self, widget, event):
|
||||
context = widget.window.cairo_create()
|
||||
self._draw(context)
|
||||
return False
|
||||
|
||||
def do_size_request(self, requisition):
|
||||
requisition.height, requisition.width = \
|
||||
gtk.icon_size_lookup(gtk.ICON_SIZE_BUTTON)
|
||||
self._text.size_request()
|
||||
|
||||
def _draw(self, context):
|
||||
rect = self.get_allocation()
|
||||
x = rect.x + rect.width * 0.5
|
||||
y = rect.y + rect.height * 0.5
|
||||
radius = rect.width / 2
|
||||
context.arc(x, y, radius, 0, 2 * math.pi)
|
||||
widget_style = self.get_style()
|
||||
context.set_source_color(widget_style.bg[self.get_state()])
|
||||
context.fill_preserve()
|
||||
|
||||
def set_text(self, text):
|
||||
self._text.set_text(str(text))
|
||||
|
||||
|
||||
class TimeoutAlert(Alert):
|
||||
"""
|
||||
This is a ready-made two button (Cancel,Continue) alert
|
||||
|
||||
It times out with a positive response after the given amount of seconds.
|
||||
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
.. code-block:: python
|
||||
from sugar.graphics.alert import TimeoutAlert
|
||||
...
|
||||
#### Method: _alert_timeout, create a Timeout alert (with ok and cancel
|
||||
buttons standard)
|
||||
# and add it to the UI.
|
||||
def _alert_timeout(self):
|
||||
#Notice that for a TimeoutAlert, you pass the number of seconds in
|
||||
#which to timeout. By default, this is 5.
|
||||
alert = TimeoutAlert(10)
|
||||
alert.props.title=_('Title of Alert Goes Here')
|
||||
alert.props.msg = _('Text message of timeout alert goes here')
|
||||
alert.connect('response', self._alert_response_cb)
|
||||
self.add_alert(alert)
|
||||
|
||||
#### Method: _alert_response_cb, called when an alert object throws a
|
||||
response event.
|
||||
def _alert_response_cb(self, alert, response_id):
|
||||
#remove the alert from the screen, since either a response button
|
||||
#was clicked or there was a timeout
|
||||
self.remove_alert(alert)
|
||||
|
||||
#Do any work that is specific to the type of button clicked.
|
||||
if response_id is gtk.RESPONSE_OK:
|
||||
print 'Ok Button was clicked. Do any work upon ok here ...'
|
||||
elif response_id is gtk.RESPONSE_CANCEL:
|
||||
print 'Cancel Button was clicked.'
|
||||
elif response_id == -1:
|
||||
print 'Timout occurred'
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, timeout=5, **kwargs):
|
||||
Alert.__init__(self, **kwargs)
|
||||
|
||||
self._timeout = timeout
|
||||
|
||||
icon = Icon(icon_name='dialog-cancel')
|
||||
self.add_button(gtk.RESPONSE_CANCEL, _('Cancel'), icon)
|
||||
icon.show()
|
||||
|
||||
self._timeout_text = _TimeoutIcon()
|
||||
self._timeout_text.set_text(self._timeout)
|
||||
self.add_button(gtk.RESPONSE_OK, _('Continue'), self._timeout_text)
|
||||
self._timeout_text.show()
|
||||
|
||||
gobject.timeout_add_seconds(1, self.__timeout)
|
||||
|
||||
def __timeout(self):
|
||||
self._timeout -= 1
|
||||
self._timeout_text.set_text(self._timeout)
|
||||
if self._timeout == 0:
|
||||
self._response(gtk.RESPONSE_OK)
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class NotifyAlert(Alert):
|
||||
"""
|
||||
Timeout alert with only an "OK" button - just for notifications
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
.. code-block:: python
|
||||
from sugar.graphics.alert import NotifyAlert
|
||||
...
|
||||
#### Method: _alert_notify, create a Notify alert (with only an 'OK'
|
||||
button)
|
||||
# and add it to the UI.
|
||||
def _alert_notify(self):
|
||||
#Notice that for a NotifyAlert, you pass the number of seconds in
|
||||
#which to notify. By default, this is 5.
|
||||
alert = NotifyAlert(10)
|
||||
alert.props.title=_('Title of Alert Goes Here')
|
||||
alert.props.msg = _('Text message of notify alert goes here')
|
||||
alert.connect('response', self._alert_response_cb)
|
||||
self.add_alert(alert)
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, timeout=5, **kwargs):
|
||||
Alert.__init__(self, **kwargs)
|
||||
|
||||
self._timeout = timeout
|
||||
|
||||
self._timeout_text = _TimeoutIcon()
|
||||
self._timeout_text.set_text(self._timeout)
|
||||
self.add_button(gtk.RESPONSE_OK, _('Ok'), self._timeout_text)
|
||||
self._timeout_text.show()
|
||||
|
||||
gobject.timeout_add(1000, self.__timeout)
|
||||
|
||||
def __timeout(self):
|
||||
self._timeout -= 1
|
||||
self._timeout_text.set_text(self._timeout)
|
||||
if self._timeout == 0:
|
||||
self._response(gtk.RESPONSE_OK)
|
||||
return False
|
||||
return True
|
||||
@@ -0,0 +1,151 @@
|
||||
# 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.
|
||||
|
||||
"""
|
||||
STABLE.
|
||||
"""
|
||||
|
||||
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, duration, fps=20, easing=EASE_OUT_EXPO):
|
||||
gobject.GObject.__init__(self)
|
||||
self._animations = []
|
||||
self._duration = duration
|
||||
self._interval = 1.0 / fps
|
||||
self._easing = easing
|
||||
self._timeout_sid = 0
|
||||
self._start_time = None
|
||||
|
||||
def add(self, animation):
|
||||
"""
|
||||
Parameter
|
||||
---------
|
||||
animation :
|
||||
|
||||
"""
|
||||
self._animations.append(animation)
|
||||
|
||||
def remove_all(self):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
None :
|
||||
|
||||
Returns
|
||||
-------
|
||||
None :
|
||||
|
||||
"""
|
||||
self.stop()
|
||||
self._animations = []
|
||||
|
||||
def start(self):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
None :
|
||||
|
||||
Returns
|
||||
-------
|
||||
None
|
||||
|
||||
"""
|
||||
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):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
None :
|
||||
|
||||
Returns
|
||||
-------
|
||||
None :
|
||||
|
||||
"""
|
||||
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._duration, time.time() - self._start_time)
|
||||
current_time = max(current_time, 0.0)
|
||||
|
||||
for animation in self._animations:
|
||||
animation.do_frame(current_time, self._duration, self._easing)
|
||||
|
||||
if current_time == self._duration:
|
||||
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, t, duration, easing):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
t:
|
||||
|
||||
duration:
|
||||
|
||||
easing:
|
||||
|
||||
Returns
|
||||
None:
|
||||
|
||||
"""
|
||||
start = self.start
|
||||
change = self.end - self.start
|
||||
|
||||
if t == duration:
|
||||
# last frame
|
||||
frame = self.end
|
||||
else:
|
||||
if easing == EASE_OUT_EXPO:
|
||||
frame = change * (-pow(2, -10 * t / duration) + 1) + start
|
||||
elif easing == EASE_IN_EXPO:
|
||||
frame = change * pow(2, 10 * (t / duration - 1)) + start
|
||||
|
||||
self.next_frame(frame)
|
||||
|
||||
def next_frame(self, frame):
|
||||
pass
|
||||
@@ -0,0 +1,41 @@
|
||||
# Copyright (C) 2008 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
|
||||
import hippo
|
||||
|
||||
from sugar.graphics import style
|
||||
|
||||
|
||||
class CanvasTextView(hippo.CanvasWidget):
|
||||
|
||||
def __init__(self, text, **kwargs):
|
||||
hippo.CanvasWidget.__init__(self, **kwargs)
|
||||
self.text_view_widget = gtk.TextView()
|
||||
self.text_view_widget.props.buffer.props.text = text
|
||||
self.text_view_widget.props.left_margin = style.DEFAULT_SPACING
|
||||
self.text_view_widget.props.right_margin = style.DEFAULT_SPACING
|
||||
self.text_view_widget.props.wrap_mode = gtk.WRAP_WORD
|
||||
self.text_view_widget.show()
|
||||
|
||||
# TODO: These fields should expand vertically instead of scrolling
|
||||
scrolled_window = gtk.ScrolledWindow()
|
||||
scrolled_window.set_shadow_type(gtk.SHADOW_OUT)
|
||||
scrolled_window.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
|
||||
scrolled_window.add(self.text_view_widget)
|
||||
|
||||
self.props.widget = scrolled_window
|
||||
@@ -0,0 +1,536 @@
|
||||
# Copyright (C) 2007, Red Hat, Inc.
|
||||
# Copyright (C) 2008, Benjamin Berg <benjamin@sipsolutions.net>
|
||||
#
|
||||
# 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 gettext
|
||||
import gtk
|
||||
import gobject
|
||||
import struct
|
||||
import logging
|
||||
|
||||
from sugar.graphics import style
|
||||
from sugar.graphics.icon import Icon
|
||||
from sugar.graphics.palette import Palette, ToolInvoker, WidgetInvoker
|
||||
|
||||
|
||||
_ = lambda msg: gettext.dgettext('sugar-toolkit', msg)
|
||||
|
||||
|
||||
def get_svg_color_string(color):
|
||||
return '#%.2X%.2X%.2X' % (color.red / 257, color.green / 257,
|
||||
color.blue / 257)
|
||||
|
||||
|
||||
class _ColorButton(gtk.Button):
|
||||
"""This is a ColorButton for Sugar. It is similar to the gtk.ColorButton,
|
||||
but does not have any alpha support.
|
||||
Instead of a color selector dialog it will pop up a Sugar palette.
|
||||
|
||||
As a preview an sugar.graphics.Icon is used. The fill color will be set to
|
||||
the current color, and the stroke color is set to the font color.
|
||||
"""
|
||||
|
||||
__gtype_name__ = 'SugarColorButton'
|
||||
__gsignals__ = {'color-set': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
|
||||
tuple())}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self._title = _('Choose a color')
|
||||
self._color = gtk.gdk.Color(0, 0, 0)
|
||||
self._has_palette = True
|
||||
self._has_invoker = True
|
||||
self._palette = None
|
||||
self._accept_drag = True
|
||||
|
||||
self._preview = Icon(icon_name='color-preview',
|
||||
icon_size=gtk.ICON_SIZE_BUTTON)
|
||||
|
||||
gobject.GObject.__init__(self, **kwargs)
|
||||
|
||||
if self._accept_drag:
|
||||
self.drag_dest_set(gtk.DEST_DEFAULT_MOTION |
|
||||
gtk.DEST_DEFAULT_HIGHLIGHT |
|
||||
gtk.DEST_DEFAULT_DROP,
|
||||
[('application/x-color', 0, 0)],
|
||||
gtk.gdk.ACTION_COPY)
|
||||
self.drag_source_set(gtk.gdk.BUTTON1_MASK | gtk.gdk.BUTTON3_MASK,
|
||||
[('application/x-color', 0, 0)],
|
||||
gtk.gdk.ACTION_COPY)
|
||||
self.connect('drag_data_received', self.__drag_data_received_cb)
|
||||
self.connect('drag_data_get', self.__drag_data_get_cb)
|
||||
|
||||
self._preview.fill_color = get_svg_color_string(self._color)
|
||||
self._preview.stroke_color = \
|
||||
get_svg_color_string(self.style.fg[gtk.STATE_NORMAL])
|
||||
self.set_image(self._preview)
|
||||
|
||||
if self._has_palette and self._has_invoker:
|
||||
self._invoker = WidgetInvoker(self)
|
||||
# FIXME: This is a hack.
|
||||
self._invoker.has_rectangle_gap = lambda: False
|
||||
self._invoker.palette = self._palette
|
||||
|
||||
def create_palette(self):
|
||||
if self._has_palette:
|
||||
self._palette = _ColorPalette(color=self._color,
|
||||
primary_text=self._title)
|
||||
self._palette.connect('color-set', self.__palette_color_set_cb)
|
||||
self._palette.connect('notify::color', self.
|
||||
__palette_color_changed)
|
||||
|
||||
return self._palette
|
||||
|
||||
def __palette_color_set_cb(self, palette):
|
||||
self.emit('color-set')
|
||||
|
||||
def __palette_color_changed(self, palette, pspec):
|
||||
self.color = self._palette.color
|
||||
|
||||
def do_style_set(self, previous_style):
|
||||
self._preview.stroke_color = \
|
||||
get_svg_color_string(self.style.fg[gtk.STATE_NORMAL])
|
||||
|
||||
def do_clicked(self):
|
||||
if self._palette:
|
||||
if not self._palette.is_up():
|
||||
self._palette.popup(immediate=True,
|
||||
state=self._palette.SECONDARY)
|
||||
else:
|
||||
self._palette.popdown(immediate=True)
|
||||
return True
|
||||
|
||||
def set_color(self, color):
|
||||
assert isinstance(color, gtk.gdk.Color)
|
||||
|
||||
if self._color.red == color.red and \
|
||||
self._color.green == color.green and \
|
||||
self._color.blue == color.blue:
|
||||
return
|
||||
|
||||
self._color = gtk.gdk.Color(color.red, color.green, color.blue)
|
||||
self._preview.fill_color = get_svg_color_string(self._color)
|
||||
if self._palette:
|
||||
self._palette.props.color = self._color
|
||||
self.notify('color')
|
||||
|
||||
def get_color(self):
|
||||
return self._color
|
||||
|
||||
color = gobject.property(type=object, getter=get_color, setter=set_color)
|
||||
|
||||
def set_icon_name(self, icon_name):
|
||||
self._preview.props.icon_name = icon_name
|
||||
|
||||
def get_icon_name(self):
|
||||
return self._preview.props.icon_name
|
||||
|
||||
icon_name = gobject.property(type=str,
|
||||
getter=get_icon_name, setter=set_icon_name)
|
||||
|
||||
def set_icon_size(self, icon_size):
|
||||
self._preview.props.icon_size = icon_size
|
||||
|
||||
def get_icon_size(self):
|
||||
return self._preview.props.icon_size
|
||||
|
||||
icon_size = gobject.property(type=int,
|
||||
getter=get_icon_size, setter=set_icon_size)
|
||||
|
||||
def set_title(self, title):
|
||||
self._title = title
|
||||
if self._palette:
|
||||
self._palette.primary_text = self._title
|
||||
|
||||
def get_title(self):
|
||||
return self._title
|
||||
|
||||
title = gobject.property(type=str, getter=get_title, setter=set_title)
|
||||
|
||||
def _set_has_invoker(self, has_invoker):
|
||||
self._has_invoker = has_invoker
|
||||
|
||||
def _get_has_invoker(self):
|
||||
return self._has_invoker
|
||||
|
||||
has_invoker = gobject.property(type=bool, default=True,
|
||||
flags=gobject.PARAM_READWRITE |
|
||||
gobject.PARAM_CONSTRUCT_ONLY,
|
||||
getter=_get_has_invoker,
|
||||
setter=_set_has_invoker)
|
||||
|
||||
def _set_has_palette(self, has_palette):
|
||||
self._has_palette = has_palette
|
||||
|
||||
def _get_has_palette(self):
|
||||
return self._has_palette
|
||||
|
||||
has_palette = gobject.property(type=bool, default=True,
|
||||
flags=gobject.PARAM_READWRITE |
|
||||
gobject.PARAM_CONSTRUCT_ONLY,
|
||||
getter=_get_has_palette,
|
||||
setter=_set_has_palette)
|
||||
|
||||
def _set_accept_drag(self, accept_drag):
|
||||
self._accept_drag = accept_drag
|
||||
|
||||
def _get_accept_drag(self):
|
||||
return self._accept_drag
|
||||
|
||||
accept_drag = gobject.property(type=bool, default=True,
|
||||
flags=gobject.PARAM_READWRITE |
|
||||
gobject.PARAM_CONSTRUCT_ONLY,
|
||||
getter=_get_accept_drag,
|
||||
setter=_set_accept_drag)
|
||||
|
||||
def __drag_begin_cb(self, widget, context):
|
||||
# Drag and Drop
|
||||
pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, True, 8,
|
||||
style.SMALL_ICON_SIZE,
|
||||
style.SMALL_ICON_SIZE)
|
||||
|
||||
red = self._color.red / 257
|
||||
green = self._color.green / 257
|
||||
blue = self._color.blue / 257
|
||||
|
||||
pixbuf.fill(red << 24 + green << 16 + blue << 8 + 0xff)
|
||||
|
||||
context.set_icon_pixbuf(pixbuf)
|
||||
|
||||
def __drag_data_get_cb(self, widget, context, selection_data, info, time):
|
||||
data = struct.pack('=HHHH', self._color.red, self._color.green,
|
||||
self._color.blue, 65535)
|
||||
selection_data.set(selection_data.target, 16, data)
|
||||
|
||||
def __drag_data_received_cb(self, widget, context, x, y, selection_data, \
|
||||
info, time):
|
||||
if len(selection_data.data) != 8:
|
||||
return
|
||||
|
||||
dropped = selection_data.data
|
||||
red = struct.unpack_from('=H', dropped, 0)[0]
|
||||
green = struct.unpack_from('=H', dropped, 2)[0]
|
||||
blue = struct.unpack_from('=H', dropped, 4)[0]
|
||||
# dropped[6] and dropped[7] is alpha, but we ignore the alpha channel
|
||||
|
||||
color = gtk.gdk.Color(red, green, blue)
|
||||
self.set_color(color)
|
||||
|
||||
|
||||
class _ColorPalette(Palette):
|
||||
"""This is a color picker palette. It will usually be used indirectly
|
||||
trough a sugar.graphics.ColorButton.
|
||||
"""
|
||||
_RED = 0
|
||||
_GREEN = 1
|
||||
_BLUE = 2
|
||||
|
||||
__gtype_name__ = 'SugarColorPalette'
|
||||
|
||||
# The color-set signal is emitted when the user is finished selecting
|
||||
# a color.
|
||||
__gsignals__ = {'color-set': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
|
||||
tuple())}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self._color = gtk.gdk.Color(0, 0, 0)
|
||||
self._previous_color = self._color.copy()
|
||||
self._scales = None
|
||||
|
||||
Palette.__init__(self, **kwargs)
|
||||
|
||||
self.connect('popup', self.__popup_cb)
|
||||
self.connect('popdown', self.__popdown_cb)
|
||||
|
||||
self._picker_hbox = gtk.HBox()
|
||||
self.set_content(self._picker_hbox)
|
||||
|
||||
self._swatch_tray = gtk.Table()
|
||||
|
||||
self._picker_hbox.pack_start(self._swatch_tray)
|
||||
self._picker_hbox.pack_start(gtk.VSeparator(),
|
||||
padding=style.DEFAULT_SPACING)
|
||||
|
||||
self._chooser_table = gtk.Table(3, 2)
|
||||
self._chooser_table.set_col_spacing(0, style.DEFAULT_PADDING)
|
||||
|
||||
self._scales = []
|
||||
self._scales.append(
|
||||
self._create_color_scale(_('Red'), self._RED, 0))
|
||||
self._scales.append(
|
||||
self._create_color_scale(_('Green'), self._GREEN, 1))
|
||||
self._scales.append(
|
||||
self._create_color_scale(_('Blue'), self._BLUE, 2))
|
||||
|
||||
self._picker_hbox.add(self._chooser_table)
|
||||
|
||||
self._picker_hbox.show_all()
|
||||
|
||||
self._build_swatches()
|
||||
|
||||
def _create_color_scale(self, text, color, row):
|
||||
label = gtk.Label(text)
|
||||
label.props.xalign = 1.0
|
||||
scale = gtk.HScale()
|
||||
scale.set_size_request(style.zoom(250), -1)
|
||||
scale.set_draw_value(False)
|
||||
scale.set_range(0, 1.0)
|
||||
scale.set_increments(0.1, 0.2)
|
||||
|
||||
if color == self._RED:
|
||||
scale.set_value(self._color.red / 65535.0)
|
||||
elif color == self._GREEN:
|
||||
scale.set_value(self._color.green / 65535.0)
|
||||
elif color == self._BLUE:
|
||||
scale.set_value(self._color.blue / 65535.0)
|
||||
|
||||
scale.connect('value-changed',
|
||||
self.__scale_value_changed_cb,
|
||||
color)
|
||||
self._chooser_table.attach(label, 0, 1, row, row + 1)
|
||||
self._chooser_table.attach(scale, 1, 2, row, row + 1)
|
||||
|
||||
return scale
|
||||
|
||||
def _build_swatches(self):
|
||||
for child in self._swatch_tray.get_children():
|
||||
child.destroy()
|
||||
|
||||
# Use a hardcoded list of colors for now.
|
||||
colors = ['#ed2529', '#69bc47', '#3c54a3',
|
||||
'#f57f25', '#0b6b3a', '#00a0c6',
|
||||
'#f6eb1a', '#b93f94', '#5b4a9c',
|
||||
'#000000', '#919496', '#ffffff']
|
||||
|
||||
# We want 3 rows of colors.
|
||||
rows = 3
|
||||
i = 0
|
||||
self._swatch_tray.props.n_rows = rows
|
||||
self._swatch_tray.props.n_columns = (len(colors) + rows - 1) / rows
|
||||
for color in colors:
|
||||
button = _ColorButton(has_palette=False,
|
||||
color=gtk.gdk.color_parse(color),
|
||||
accept_drag=False,
|
||||
icon_size=gtk.ICON_SIZE_LARGE_TOOLBAR)
|
||||
button.set_relief(gtk.RELIEF_NONE)
|
||||
self._swatch_tray.attach(button,
|
||||
i % rows, i % rows + 1,
|
||||
i / rows, i / rows + 1,
|
||||
yoptions=0, xoptions=0)
|
||||
button.connect('clicked', self.__swatch_button_clicked_cb)
|
||||
i += 1
|
||||
|
||||
self._swatch_tray.show_all()
|
||||
|
||||
def __popup_cb(self, palette):
|
||||
self._previous_color = self._color.copy()
|
||||
|
||||
def __popdown_cb(self, palette):
|
||||
self.emit('color-set')
|
||||
|
||||
def __scale_value_changed_cb(self, widget, color):
|
||||
new_color = self._color.copy()
|
||||
if color == self._RED:
|
||||
new_color.red = int(65535 * widget.get_value())
|
||||
elif color == self._GREEN:
|
||||
new_color.green = int(65535 * widget.get_value())
|
||||
elif color == self._BLUE:
|
||||
new_color.blue = int(65535 * widget.get_value())
|
||||
self.color = new_color
|
||||
|
||||
def do_key_press_event(self, event):
|
||||
if event.keyval == gtk.keysyms.Escape:
|
||||
self.props.color = self._previous_color
|
||||
self.popdown(immediate=True)
|
||||
return True
|
||||
elif event.keyval == gtk.keysyms.Return:
|
||||
self.popdown(immediate=True)
|
||||
return True
|
||||
return False
|
||||
|
||||
def __swatch_button_clicked_cb(self, button):
|
||||
self.props.color = button.get_color()
|
||||
|
||||
def set_color(self, color):
|
||||
assert isinstance(color, gtk.gdk.Color)
|
||||
|
||||
if self._color.red == color.red and \
|
||||
self._color.green == color.green and \
|
||||
self._color.blue == color.blue:
|
||||
return
|
||||
|
||||
self._color = color.copy()
|
||||
|
||||
if self._scales:
|
||||
self._scales[self._RED].set_value(self._color.red / 65535.0)
|
||||
self._scales[self._GREEN].set_value(self._color.green / 65535.0)
|
||||
self._scales[self._BLUE].set_value(self._color.blue / 65535.0)
|
||||
|
||||
self.notify('color')
|
||||
|
||||
def get_color(self):
|
||||
return self._color
|
||||
|
||||
color = gobject.property(type=object, getter=get_color, setter=set_color)
|
||||
|
||||
|
||||
def _add_accelerator(tool_button):
|
||||
if not tool_button.props.accelerator or not tool_button.get_toplevel() or \
|
||||
not tool_button.child:
|
||||
return
|
||||
|
||||
# TODO: should we remove the accelerator from the prev top level?
|
||||
|
||||
accel_group = tool_button.get_toplevel().get_data('sugar-accel-group')
|
||||
if not accel_group:
|
||||
logging.warning('No gtk.AccelGroup in the top level window.')
|
||||
return
|
||||
|
||||
keyval, mask = gtk.accelerator_parse(tool_button.props.accelerator)
|
||||
# the accelerator needs to be set at the child, so the gtk.AccelLabel
|
||||
# in the palette can pick it up.
|
||||
tool_button.child.add_accelerator('clicked', accel_group, keyval, mask,
|
||||
gtk.ACCEL_LOCKED | gtk.ACCEL_VISIBLE)
|
||||
|
||||
|
||||
def _hierarchy_changed_cb(tool_button, previous_toplevel):
|
||||
_add_accelerator(tool_button)
|
||||
|
||||
|
||||
def setup_accelerator(tool_button):
|
||||
_add_accelerator(tool_button)
|
||||
tool_button.connect('hierarchy-changed', _hierarchy_changed_cb)
|
||||
|
||||
|
||||
class ColorToolButton(gtk.ToolItem):
|
||||
# This not ideal. It would be better to subclass gtk.ToolButton, however
|
||||
# the python bindings do not seem to be powerfull enough for that.
|
||||
# (As we need to change a variable in the class structure.)
|
||||
|
||||
__gtype_name__ = 'SugarColorToolButton'
|
||||
__gsignals__ = {'color-set': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
|
||||
tuple())}
|
||||
|
||||
def __init__(self, icon_name='color-preview', **kwargs):
|
||||
self._accelerator = None
|
||||
self._tooltip = None
|
||||
self._palette_invoker = ToolInvoker()
|
||||
self._palette = None
|
||||
|
||||
gobject.GObject.__init__(self, **kwargs)
|
||||
|
||||
# The gtk.ToolButton has already added a normal button.
|
||||
# Replace it with a ColorButton
|
||||
color_button = _ColorButton(icon_name=icon_name, has_invoker=False)
|
||||
self.add(color_button)
|
||||
|
||||
# The following is so that the behaviour on the toolbar is correct.
|
||||
color_button.set_relief(gtk.RELIEF_NONE)
|
||||
color_button.icon_size = gtk.ICON_SIZE_LARGE_TOOLBAR
|
||||
|
||||
self._palette_invoker.attach_tool(self)
|
||||
|
||||
# This widget just proxies the following properties to the colorbutton
|
||||
color_button.connect('notify::color', self.__notify_change)
|
||||
color_button.connect('notify::icon-name', self.__notify_change)
|
||||
color_button.connect('notify::icon-size', self.__notify_change)
|
||||
color_button.connect('notify::title', self.__notify_change)
|
||||
color_button.connect('color-set', self.__color_set_cb)
|
||||
color_button.connect('can-activate-accel',
|
||||
self.__button_can_activate_accel_cb)
|
||||
|
||||
def __button_can_activate_accel_cb(self, button, signal_id):
|
||||
# Accept activation via accelerators regardless of this widget's state
|
||||
return True
|
||||
|
||||
def set_accelerator(self, accelerator):
|
||||
self._accelerator = accelerator
|
||||
setup_accelerator(self)
|
||||
|
||||
def get_accelerator(self):
|
||||
return self._accelerator
|
||||
|
||||
accelerator = gobject.property(type=str, setter=set_accelerator,
|
||||
getter=get_accelerator)
|
||||
|
||||
def create_palette(self):
|
||||
self._palette = self.get_child().create_palette()
|
||||
return self._palette
|
||||
|
||||
def get_palette_invoker(self):
|
||||
return self._palette_invoker
|
||||
|
||||
def set_palette_invoker(self, palette_invoker):
|
||||
self._palette_invoker.detach()
|
||||
self._palette_invoker = palette_invoker
|
||||
|
||||
palette_invoker = gobject.property(
|
||||
type=object, setter=set_palette_invoker, getter=get_palette_invoker)
|
||||
|
||||
def set_color(self, color):
|
||||
self.get_child().props.color = color
|
||||
|
||||
def get_color(self):
|
||||
return self.get_child().props.color
|
||||
|
||||
color = gobject.property(type=object, getter=get_color, setter=set_color)
|
||||
|
||||
def set_icon_name(self, icon_name):
|
||||
self.get_child().props.icon_name = icon_name
|
||||
|
||||
def get_icon_name(self):
|
||||
return self.get_child().props.icon_name
|
||||
|
||||
icon_name = gobject.property(type=str,
|
||||
getter=get_icon_name, setter=set_icon_name)
|
||||
|
||||
def set_icon_size(self, icon_size):
|
||||
self.get_child().props.icon_size = icon_size
|
||||
|
||||
def get_icon_size(self):
|
||||
return self.get_child().props.icon_size
|
||||
|
||||
icon_size = gobject.property(type=int,
|
||||
getter=get_icon_size, setter=set_icon_size)
|
||||
|
||||
def set_title(self, title):
|
||||
self.get_child().props.title = title
|
||||
|
||||
def get_title(self):
|
||||
return self.get_child().props.title
|
||||
|
||||
title = gobject.property(type=str, getter=get_title, setter=set_title)
|
||||
|
||||
def do_expose_event(self, event):
|
||||
child = self.get_child()
|
||||
allocation = self.get_allocation()
|
||||
if self._palette and self._palette.is_up():
|
||||
invoker = self._palette.props.invoker
|
||||
invoker.draw_rectangle(event, self._palette)
|
||||
elif child.state == gtk.STATE_PRELIGHT:
|
||||
child.style.paint_box(event.window, gtk.STATE_PRELIGHT,
|
||||
gtk.SHADOW_NONE, event.area,
|
||||
child, 'toolbutton-prelight',
|
||||
allocation.x, allocation.y,
|
||||
allocation.width, allocation.height)
|
||||
|
||||
gtk.ToolButton.do_expose_event(self, event)
|
||||
|
||||
def __notify_change(self, widget, pspec):
|
||||
self.notify(pspec.name)
|
||||
|
||||
def __color_set_cb(self, widget):
|
||||
self.emit('color-set')
|
||||
@@ -0,0 +1,170 @@
|
||||
# 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.
|
||||
|
||||
"""
|
||||
STABLE.
|
||||
"""
|
||||
|
||||
import gobject
|
||||
import gtk
|
||||
|
||||
|
||||
class ComboBox(gtk.ComboBox):
|
||||
|
||||
__gtype_name__ = 'SugarComboBox'
|
||||
|
||||
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 get_value(self):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
None :
|
||||
|
||||
Returns:
|
||||
--------
|
||||
value :
|
||||
|
||||
"""
|
||||
row = self.get_active_item()
|
||||
if not row:
|
||||
return None
|
||||
return row[0]
|
||||
|
||||
value = gobject.property(
|
||||
type=object, getter=get_value, setter=None)
|
||||
|
||||
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, max(width, height), 0)
|
||||
if not info:
|
||||
raise ValueError('Icon %r not found.' % name)
|
||||
fname = info.get_filename()
|
||||
del info
|
||||
return fname
|
||||
|
||||
def append_item(self, action_id, text, icon_name=None, file_name=None):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
action_id :
|
||||
|
||||
text :
|
||||
|
||||
icon_name=None :
|
||||
|
||||
file_name=None :
|
||||
|
||||
Returns
|
||||
-------
|
||||
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 = max(w, h)
|
||||
|
||||
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):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
None
|
||||
|
||||
Returns
|
||||
-------
|
||||
None
|
||||
|
||||
"""
|
||||
self._model.append([0, None, None, True])
|
||||
|
||||
def get_active_item(self):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
None
|
||||
|
||||
Returns
|
||||
-------
|
||||
Active_item :
|
||||
|
||||
"""
|
||||
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):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
None
|
||||
|
||||
Returns
|
||||
-------
|
||||
None
|
||||
|
||||
"""
|
||||
self._model.clear()
|
||||
|
||||
def _is_separator(self, model, row):
|
||||
return model[row][3]
|
||||
@@ -0,0 +1,41 @@
|
||||
# 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.
|
||||
|
||||
"""
|
||||
STABLE.
|
||||
"""
|
||||
|
||||
import gtk
|
||||
import hippo
|
||||
|
||||
|
||||
class CanvasEntry(hippo.CanvasEntry):
|
||||
|
||||
def set_background(self, color_spec):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
color_spec :
|
||||
|
||||
Returns
|
||||
-------
|
||||
None
|
||||
|
||||
"""
|
||||
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)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,98 @@
|
||||
# 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 import style
|
||||
from sugar.graphics.icon import _SVGLoader
|
||||
|
||||
ICON_ENTRY_PRIMARY = gtk.ENTRY_ICON_PRIMARY
|
||||
ICON_ENTRY_SECONDARY = gtk.ENTRY_ICON_SECONDARY
|
||||
|
||||
|
||||
class IconEntry(gtk.Entry):
|
||||
|
||||
def __init__(self):
|
||||
gtk.Entry.__init__(self)
|
||||
|
||||
self._clear_icon = None
|
||||
self._clear_shown = False
|
||||
|
||||
self.connect('key_press_event', self._keypress_event_cb)
|
||||
|
||||
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)
|
||||
|
||||
if icon_info.get_filename().endswith('.svg'):
|
||||
loader = _SVGLoader()
|
||||
entities = {'fill_color': style.COLOR_TOOLBAR_GREY.get_svg(),
|
||||
'stroke_color': style.COLOR_TOOLBAR_GREY.get_svg()}
|
||||
handle = loader.load(icon_info.get_filename(), entities, None)
|
||||
pixbuf = handle.get_pixbuf()
|
||||
else:
|
||||
pixbuf = gtk.gdk.pixbuf_new_from_file(icon_info.get_filename())
|
||||
del icon_info
|
||||
self.set_icon(position, pixbuf)
|
||||
|
||||
def set_icon(self, position, pixbuf):
|
||||
if type(pixbuf) is not gtk.gdk.Pixbuf:
|
||||
raise ValueError('Argument must be a pixbuf, not %r.' % pixbuf)
|
||||
self.set_icon_from_pixbuf(position, pixbuf)
|
||||
|
||||
def remove_icon(self, position):
|
||||
self.set_icon_from_pixbuf(position, None)
|
||||
|
||||
def add_clear_button(self):
|
||||
if self.props.text != "":
|
||||
self.show_clear_button()
|
||||
else:
|
||||
self.hide_clear_button()
|
||||
|
||||
self.connect('icon-press', self._icon_pressed_cb)
|
||||
self.connect('changed', self._changed_cb)
|
||||
|
||||
def show_clear_button(self):
|
||||
if not self._clear_shown:
|
||||
self.set_icon_from_name(ICON_ENTRY_SECONDARY,
|
||||
'dialog-cancel')
|
||||
self._clear_shown = True
|
||||
|
||||
def hide_clear_button(self):
|
||||
if self._clear_shown:
|
||||
self.remove_icon(ICON_ENTRY_SECONDARY)
|
||||
self._clear_shown = False
|
||||
|
||||
def _keypress_event_cb(self, widget, event):
|
||||
keyval = gtk.gdk.keyval_name(event.keyval)
|
||||
if keyval == 'Escape':
|
||||
self.props.text = ''
|
||||
return True
|
||||
return False
|
||||
|
||||
def _icon_pressed_cb(self, entru, icon_pos, button):
|
||||
if icon_pos == ICON_ENTRY_SECONDARY:
|
||||
self.set_text('')
|
||||
self.hide_clear_button()
|
||||
|
||||
def _changed_cb(self, icon_entry):
|
||||
if not self.props.text:
|
||||
self.hide_clear_button()
|
||||
else:
|
||||
self.show_clear_button()
|
||||
@@ -0,0 +1,95 @@
|
||||
# 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.
|
||||
|
||||
"""
|
||||
STABLE.
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
import gobject
|
||||
import pango
|
||||
import gtk
|
||||
|
||||
from sugar.graphics.icon import Icon
|
||||
|
||||
|
||||
class MenuItem(gtk.ImageMenuItem):
|
||||
|
||||
def __init__(self, text_label=None, icon_name=None, text_maxlen=60,
|
||||
xo_color=None, file_name=None):
|
||||
gobject.GObject.__init__(self)
|
||||
self._accelerator = None
|
||||
|
||||
label = gtk.AccelLabel(text_label)
|
||||
label.set_alignment(0.0, 0.5)
|
||||
label.set_accel_widget(self)
|
||||
if text_maxlen > 0:
|
||||
label.set_ellipsize(pango.ELLIPSIZE_MIDDLE)
|
||||
label.set_max_width_chars(text_maxlen)
|
||||
self.add(label)
|
||||
label.show()
|
||||
|
||||
if icon_name is not None:
|
||||
icon = Icon(icon_name=icon_name,
|
||||
icon_size=gtk.ICON_SIZE_SMALL_TOOLBAR)
|
||||
if xo_color is not None:
|
||||
icon.props.xo_color = xo_color
|
||||
self.set_image(icon)
|
||||
icon.show()
|
||||
|
||||
elif file_name is not None:
|
||||
icon = Icon(file=file_name, icon_size=gtk.ICON_SIZE_SMALL_TOOLBAR)
|
||||
if xo_color is not None:
|
||||
icon.props.xo_color = xo_color
|
||||
self.set_image(icon)
|
||||
icon.show()
|
||||
|
||||
self.connect('can-activate-accel', self.__can_activate_accel_cb)
|
||||
self.connect('hierarchy-changed', self.__hierarchy_changed_cb)
|
||||
|
||||
def __hierarchy_changed_cb(self, widget, previous_toplevel):
|
||||
self._add_accelerator()
|
||||
|
||||
def __can_activate_accel_cb(self, widget, signal_id):
|
||||
# Accept activation via accelerators regardless of this widget's state
|
||||
return True
|
||||
|
||||
def _add_accelerator(self):
|
||||
if self._accelerator is None or self.get_toplevel() is None:
|
||||
return
|
||||
|
||||
# TODO: should we remove the accelerator from the prev top level?
|
||||
|
||||
accel_group = self.get_toplevel().get_data('sugar-accel-group')
|
||||
if not accel_group:
|
||||
logging.warning('No gtk.AccelGroup in the top level window.')
|
||||
return
|
||||
|
||||
keyval, mask = gtk.accelerator_parse(self._accelerator)
|
||||
self.add_accelerator('activate', accel_group, keyval, mask,
|
||||
gtk.ACCEL_LOCKED | gtk.ACCEL_VISIBLE)
|
||||
|
||||
def set_accelerator(self, accelerator):
|
||||
self._accelerator = accelerator
|
||||
self._add_accelerator()
|
||||
|
||||
def get_accelerator(self):
|
||||
return self._accelerator
|
||||
|
||||
accelerator = gobject.property(type=str, setter=set_accelerator,
|
||||
getter=get_accelerator)
|
||||
@@ -0,0 +1,151 @@
|
||||
# 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.
|
||||
|
||||
"""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)
|
||||
|
||||
STABLE.
|
||||
"""
|
||||
|
||||
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)
|
||||
|
||||
self._can_close_tabs = None
|
||||
|
||||
self.set_scrollable(True)
|
||||
self.show()
|
||||
|
||||
def do_set_property(self, pspec, value):
|
||||
"""
|
||||
Set notebook property
|
||||
|
||||
Parameters
|
||||
----------
|
||||
pspec :
|
||||
property for which the value will be set
|
||||
|
||||
Returns
|
||||
-------
|
||||
None
|
||||
|
||||
Raises
|
||||
------
|
||||
AssertionError
|
||||
|
||||
"""
|
||||
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)
|
||||
|
||||
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):
|
||||
"""
|
||||
Adds a page to the notebook.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
text_label :
|
||||
|
||||
widget :
|
||||
|
||||
Returns
|
||||
-------
|
||||
Boolean
|
||||
Returns TRUE if the page is successfully added to th notebook.
|
||||
|
||||
"""
|
||||
# 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,132 @@
|
||||
# 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.
|
||||
|
||||
"""
|
||||
STABLE.
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
import gobject
|
||||
import gtk
|
||||
import dbus
|
||||
|
||||
from sugar.datastore import datastore
|
||||
|
||||
|
||||
J_DBUS_SERVICE = 'org.laptop.Journal'
|
||||
J_DBUS_INTERFACE = 'org.laptop.Journal'
|
||||
J_DBUS_PATH = '/org/laptop/Journal'
|
||||
|
||||
|
||||
class ObjectChooser(object):
|
||||
|
||||
def __init__(self, title=None, parent=None, flags=None, buttons=None,
|
||||
what_filter=None):
|
||||
# For backwards compatibility:
|
||||
# - We ignore title, flags and buttons.
|
||||
# - 'parent' can be a xid or a gtk.Window
|
||||
|
||||
if title is not None or flags is not None or buttons is not None:
|
||||
logging.warning('Invocation of ObjectChooser() has deprecated '
|
||||
'parameters.')
|
||||
|
||||
if parent is None:
|
||||
parent_xid = 0
|
||||
elif hasattr(parent, 'window') and hasattr(parent.window, 'xid'):
|
||||
parent_xid = parent.window.xid
|
||||
else:
|
||||
parent_xid = parent
|
||||
|
||||
self._parent_xid = parent_xid
|
||||
self._main_loop = None
|
||||
self._object_id = None
|
||||
self._bus = None
|
||||
self._chooser_id = None
|
||||
self._response_code = gtk.RESPONSE_NONE
|
||||
self._what_filter = what_filter
|
||||
|
||||
def run(self):
|
||||
self._object_id = None
|
||||
|
||||
self._main_loop = gobject.MainLoop()
|
||||
|
||||
self._bus = dbus.SessionBus(mainloop=self._main_loop)
|
||||
self._bus.add_signal_receiver(
|
||||
self.__name_owner_changed_cb,
|
||||
signal_name='NameOwnerChanged',
|
||||
dbus_interface='org.freedesktop.DBus',
|
||||
arg0=J_DBUS_SERVICE)
|
||||
|
||||
obj = self._bus.get_object(J_DBUS_SERVICE, J_DBUS_PATH)
|
||||
journal = dbus.Interface(obj, J_DBUS_INTERFACE)
|
||||
journal.connect_to_signal('ObjectChooserResponse',
|
||||
self.__chooser_response_cb)
|
||||
journal.connect_to_signal('ObjectChooserCancelled',
|
||||
self.__chooser_cancelled_cb)
|
||||
|
||||
if self._what_filter is None:
|
||||
what_filter = ''
|
||||
else:
|
||||
what_filter = self._what_filter
|
||||
|
||||
self._chooser_id = journal.ChooseObject(self._parent_xid, what_filter)
|
||||
|
||||
gtk.gdk.threads_leave()
|
||||
try:
|
||||
self._main_loop.run()
|
||||
finally:
|
||||
gtk.gdk.threads_enter()
|
||||
self._main_loop = None
|
||||
|
||||
return self._response_code
|
||||
|
||||
def get_selected_object(self):
|
||||
if self._object_id is None:
|
||||
return None
|
||||
else:
|
||||
return datastore.get(self._object_id)
|
||||
|
||||
def destroy(self):
|
||||
self._cleanup()
|
||||
|
||||
def _cleanup(self):
|
||||
if self._main_loop is not None:
|
||||
self._main_loop.quit()
|
||||
self._main_loop = None
|
||||
self._bus = None
|
||||
|
||||
def __chooser_response_cb(self, chooser_id, object_id):
|
||||
if chooser_id != self._chooser_id:
|
||||
return
|
||||
logging.debug('ObjectChooser.__chooser_response_cb: %r', object_id)
|
||||
self._response_code = gtk.RESPONSE_ACCEPT
|
||||
self._object_id = object_id
|
||||
self._cleanup()
|
||||
|
||||
def __chooser_cancelled_cb(self, chooser_id):
|
||||
if chooser_id != self._chooser_id:
|
||||
return
|
||||
logging.debug('ObjectChooser.__chooser_cancelled_cb: %r', chooser_id)
|
||||
self._response_code = gtk.RESPONSE_CANCEL
|
||||
self._cleanup()
|
||||
|
||||
def __name_owner_changed_cb(self, name, old, new):
|
||||
logging.debug('ObjectChooser.__name_owner_changed_cb')
|
||||
# Journal service disappeared from the bus
|
||||
self._response_code = gtk.RESPONSE_CANCEL
|
||||
self._cleanup()
|
||||
@@ -0,0 +1,445 @@
|
||||
# Copyright (C) 2007, Eduardo Silva <edsiper@gmail.com>
|
||||
# Copyright (C) 2008, One Laptop Per Child
|
||||
# Copyright (C) 2009, Tomeu Vizoso
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""
|
||||
STABLE.
|
||||
"""
|
||||
|
||||
import gtk
|
||||
import gobject
|
||||
import pango
|
||||
|
||||
from sugar.graphics import palettegroup
|
||||
from sugar.graphics import animator
|
||||
from sugar.graphics import style
|
||||
from sugar.graphics.icon import Icon
|
||||
from sugar.graphics.palettewindow import PaletteWindow
|
||||
from sugar import _sugarext
|
||||
|
||||
# DEPRECATED
|
||||
# Import these for backwards compatibility
|
||||
from sugar.graphics.palettewindow import MouseSpeedDetector, Invoker, \
|
||||
WidgetInvoker, CanvasInvoker, ToolInvoker, CellRendererInvoker
|
||||
|
||||
|
||||
class Palette(PaletteWindow):
|
||||
PRIMARY = 0
|
||||
SECONDARY = 1
|
||||
|
||||
__gtype_name__ = 'SugarPalette'
|
||||
|
||||
def __init__(self, label=None, accel_path=None, menu_after_content=False,
|
||||
text_maxlen=60, **kwargs):
|
||||
# DEPRECATED: label is passed with the primary-text property,
|
||||
# accel_path is set via the invoker property, and menu_after_content
|
||||
# is not used
|
||||
|
||||
self._primary_text = None
|
||||
self._secondary_text = None
|
||||
self._icon = None
|
||||
self._icon_visible = True
|
||||
self._palette_state = self.PRIMARY
|
||||
|
||||
palette_box = gtk.VBox()
|
||||
|
||||
primary_box = gtk.HBox()
|
||||
palette_box.pack_start(primary_box, expand=False)
|
||||
primary_box.show()
|
||||
|
||||
self._icon_box = gtk.HBox()
|
||||
self._icon_box.set_size_request(style.GRID_CELL_SIZE, -1)
|
||||
primary_box.pack_start(self._icon_box, expand=False)
|
||||
|
||||
labels_box = gtk.VBox()
|
||||
self._label_alignment = gtk.Alignment(xalign=0, yalign=0.5,
|
||||
xscale=1, yscale=0.33)
|
||||
self._label_alignment.set_padding(0, 0, style.DEFAULT_SPACING,
|
||||
style.DEFAULT_SPACING)
|
||||
self._label_alignment.add(labels_box)
|
||||
self._label_alignment.show()
|
||||
primary_box.pack_start(self._label_alignment, expand=True)
|
||||
labels_box.show()
|
||||
|
||||
self._label = gtk.AccelLabel('')
|
||||
self._label.set_alignment(0, 0.5)
|
||||
|
||||
if text_maxlen > 0:
|
||||
self._label.set_max_width_chars(text_maxlen)
|
||||
self._label.set_ellipsize(pango.ELLIPSIZE_MIDDLE)
|
||||
labels_box.pack_start(self._label, expand=True)
|
||||
|
||||
self._secondary_label = gtk.Label()
|
||||
self._secondary_label.set_alignment(0, 0.5)
|
||||
|
||||
if text_maxlen > 0:
|
||||
self._secondary_label.set_max_width_chars(text_maxlen)
|
||||
self._secondary_label.set_ellipsize(pango.ELLIPSIZE_END)
|
||||
|
||||
labels_box.pack_start(self._secondary_label, expand=True)
|
||||
|
||||
self._secondary_box = gtk.VBox()
|
||||
palette_box.pack_start(self._secondary_box)
|
||||
|
||||
self._separator = gtk.HSeparator()
|
||||
self._secondary_box.pack_start(self._separator)
|
||||
|
||||
self._menu_content_separator = gtk.HSeparator()
|
||||
|
||||
self._secondary_anim = animator.Animator(2.0, 10)
|
||||
self._secondary_anim.add(_SecondaryAnimation(self))
|
||||
|
||||
# we init after initializing all of our containers
|
||||
PaletteWindow.__init__(self, **kwargs)
|
||||
|
||||
primary_box.set_size_request(-1, style.GRID_CELL_SIZE
|
||||
- 2 * self.get_border_width())
|
||||
|
||||
self._full_request = [0, 0]
|
||||
self._menu_box = None
|
||||
self._content = None
|
||||
|
||||
# we set these for backward compatibility
|
||||
if label is not None:
|
||||
self.props.primary_text = label
|
||||
|
||||
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(palette_box)
|
||||
palette_box.show()
|
||||
|
||||
# The menu is not shown here until an item is added
|
||||
self.menu = _Menu(self)
|
||||
self.menu.connect('item-inserted', self.__menu_item_inserted_cb)
|
||||
|
||||
self.connect('realize', self.__realize_cb)
|
||||
self.connect('show', self.__show_cb)
|
||||
self.connect('hide', self.__hide_cb)
|
||||
self.connect('notify::invoker', self.__notify_invoker_cb)
|
||||
self.connect('destroy', self.__destroy_cb)
|
||||
|
||||
def _invoker_right_click_cb(self, invoker):
|
||||
self.popup(immediate=True, state=self.SECONDARY)
|
||||
|
||||
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.get_style().xthickness)
|
||||
|
||||
def __menu_item_inserted_cb(self, menu):
|
||||
self._update_separators()
|
||||
|
||||
def __destroy_cb(self, palette):
|
||||
self._secondary_anim.stop()
|
||||
self.popdown(immediate=True)
|
||||
# Break the reference cycle. It looks like the gc is not able to free
|
||||
# it, possibly because gtk.Menu memory handling is very special.
|
||||
self.menu.disconnect_by_func(self.__menu_item_inserted_cb)
|
||||
self.menu = None
|
||||
|
||||
def __show_cb(self, widget):
|
||||
self.menu.set_active(True)
|
||||
|
||||
def __hide_cb(self, widget):
|
||||
self.menu.set_active(False)
|
||||
self.menu.cancel()
|
||||
self._secondary_anim.stop()
|
||||
|
||||
def __notify_invoker_cb(self, palette, pspec):
|
||||
invoker = self.props.invoker
|
||||
if invoker is not None and hasattr(invoker.props, 'widget'):
|
||||
self._update_accel_widget()
|
||||
self._invoker.connect('notify::widget',
|
||||
self.__invoker_widget_changed_cb)
|
||||
|
||||
def __invoker_widget_changed_cb(self, invoker, spec):
|
||||
self._update_accel_widget()
|
||||
|
||||
def get_full_size_request(self):
|
||||
return self._full_request
|
||||
|
||||
def popup(self, immediate=False, state=None):
|
||||
if self._invoker is not None:
|
||||
self._update_full_request()
|
||||
|
||||
PaletteWindow.popup(self, immediate)
|
||||
|
||||
if state is None:
|
||||
state = self.PRIMARY
|
||||
self.set_palette_state(state)
|
||||
|
||||
if state == self.PRIMARY:
|
||||
self._secondary_anim.start()
|
||||
else:
|
||||
self._secondary_anim.stop()
|
||||
|
||||
def popdown(self, immediate=False):
|
||||
if immediate:
|
||||
self._secondary_anim.stop()
|
||||
self._popdown_submenus()
|
||||
# to suppress glitches while later re-opening
|
||||
self.set_palette_state(self.PRIMARY)
|
||||
PaletteWindow.popdown(self, immediate)
|
||||
|
||||
def _popdown_submenus(self):
|
||||
# TODO explicit hiding of subitems
|
||||
# should be removed after fixing #1301
|
||||
if self.menu is not None:
|
||||
for menu_item in self.menu.get_children():
|
||||
if menu_item.props.submenu is not None:
|
||||
menu_item.props.submenu.popdown()
|
||||
|
||||
def on_enter(self, event):
|
||||
PaletteWindow.on_enter(self, event)
|
||||
self._secondary_anim.start()
|
||||
|
||||
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.DEFAULT_SPACING)
|
||||
self._secondary_box.pack_start(self._content)
|
||||
|
||||
def _update_accel_widget(self):
|
||||
assert self.props.invoker is not None
|
||||
self._label.props.accel_widget = self.props.invoker.props.widget
|
||||
|
||||
def set_primary_text(self, label, accel_path=None):
|
||||
self._primary_text = label
|
||||
|
||||
if label is not None:
|
||||
self._label.set_markup('<b>%s</b>' % label)
|
||||
self._label.show()
|
||||
|
||||
def get_primary_text(self):
|
||||
return self._primary_text
|
||||
|
||||
primary_text = gobject.property(type=str,
|
||||
getter=get_primary_text,
|
||||
setter=set_primary_text)
|
||||
|
||||
def set_secondary_text(self, label):
|
||||
if label is not None:
|
||||
label = label.split('\n', 1)[0]
|
||||
self._secondary_text = label
|
||||
|
||||
if label is None:
|
||||
self._secondary_label.hide()
|
||||
else:
|
||||
self._secondary_label.set_text(label)
|
||||
self._secondary_label.show()
|
||||
|
||||
def get_secondary_text(self):
|
||||
return self._secondary_text
|
||||
|
||||
secondary_text = gobject.property(type=str, getter=get_secondary_text,
|
||||
setter=set_secondary_text)
|
||||
|
||||
def _show_icon(self):
|
||||
self._label_alignment.set_padding(0, 0, 0, style.DEFAULT_SPACING)
|
||||
self._icon_box.show()
|
||||
|
||||
def _hide_icon(self):
|
||||
self._icon_box.hide()
|
||||
self._label_alignment.set_padding(0, 0, style.DEFAULT_SPACING,
|
||||
style.DEFAULT_SPACING)
|
||||
|
||||
def set_icon(self, icon):
|
||||
if icon is None:
|
||||
self._icon = None
|
||||
self._hide_icon()
|
||||
else:
|
||||
if self._icon:
|
||||
self._icon_box.remove(self._icon_box.get_children()[0])
|
||||
|
||||
event_box = gtk.EventBox()
|
||||
event_box.connect('button-release-event',
|
||||
self.__icon_button_release_event_cb)
|
||||
self._icon_box.pack_start(event_box)
|
||||
event_box.show()
|
||||
|
||||
self._icon = icon
|
||||
self._icon.props.icon_size = gtk.ICON_SIZE_LARGE_TOOLBAR
|
||||
event_box.add(self._icon)
|
||||
self._icon.show()
|
||||
self._show_icon()
|
||||
|
||||
def get_icon(self):
|
||||
return self._icon
|
||||
|
||||
icon = gobject.property(type=object, getter=get_icon, setter=set_icon)
|
||||
|
||||
def __icon_button_release_event_cb(self, icon, event):
|
||||
self.emit('activate')
|
||||
|
||||
def set_icon_visible(self, visible):
|
||||
self._icon_visible = visible
|
||||
|
||||
if visible and self._icon is not None:
|
||||
self._show_icon()
|
||||
else:
|
||||
self._hide_icon()
|
||||
|
||||
def get_icon_visible(self):
|
||||
return self._icon_visilbe
|
||||
|
||||
icon_visible = gobject.property(type=bool,
|
||||
default=True,
|
||||
getter=get_icon_visible,
|
||||
setter=set_icon_visible)
|
||||
|
||||
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 do_size_request(self, requisition):
|
||||
PaletteWindow.do_size_request(self, requisition)
|
||||
|
||||
# gtk.AccelLabel request doesn't include the accelerator.
|
||||
label_width = self._label_alignment.size_request()[0] + \
|
||||
self._label.get_accel_width() + \
|
||||
2 * self.get_border_width()
|
||||
|
||||
requisition.width = max(requisition.width,
|
||||
label_width,
|
||||
self._full_request[0])
|
||||
|
||||
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._update_accept_focus()
|
||||
|
||||
def _update_full_request(self):
|
||||
if self._palette_state == self.PRIMARY:
|
||||
self.menu.embed(self._menu_box)
|
||||
self._secondary_box.show()
|
||||
|
||||
self._full_request = self.size_request()
|
||||
|
||||
if self._palette_state == self.PRIMARY:
|
||||
self.menu.unembed()
|
||||
self._secondary_box.hide()
|
||||
|
||||
def _set_palette_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.update_position()
|
||||
|
||||
self._palette_state = state
|
||||
|
||||
|
||||
class PaletteActionBar(gtk.HButtonBox):
|
||||
|
||||
def add_action(self, label, icon_name=None):
|
||||
button = gtk.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'
|
||||
|
||||
__gsignals__ = {
|
||||
'item-inserted': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])),
|
||||
}
|
||||
|
||||
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.emit('item-inserted')
|
||||
self.show()
|
||||
|
||||
def attach(self, child, left_attach, right_attach,
|
||||
top_attach, bottom_attach):
|
||||
_sugarext.Menu.attach(self, child, left_attach, right_attach,
|
||||
top_attach, bottom_attach)
|
||||
self.emit('item-inserted')
|
||||
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 _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_palette_state(Palette.SECONDARY)
|
||||
@@ -0,0 +1,106 @@
|
||||
# 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.
|
||||
|
||||
"""
|
||||
STABLE.
|
||||
"""
|
||||
|
||||
import gobject
|
||||
|
||||
|
||||
_groups = {}
|
||||
|
||||
|
||||
def get_group(group_id):
|
||||
if group_id in _groups:
|
||||
group = _groups[group_id]
|
||||
else:
|
||||
group = Group()
|
||||
_groups[group_id] = group
|
||||
|
||||
return group
|
||||
|
||||
|
||||
def popdown_all():
|
||||
for group in _groups.values():
|
||||
group.popdown()
|
||||
|
||||
|
||||
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)
|
||||
del self._sig_ids[palette]
|
||||
|
||||
def popdown(self):
|
||||
for palette in self._palettes:
|
||||
if palette.is_up():
|
||||
palette.popdown(immediate=True)
|
||||
|
||||
def _palette_popup_cb(self, palette):
|
||||
for i in self._palettes:
|
||||
if i != palette:
|
||||
i.popdown(immediate=True)
|
||||
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')
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,30 @@
|
||||
# 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.
|
||||
|
||||
"""
|
||||
STABLE.
|
||||
"""
|
||||
|
||||
import gtk
|
||||
|
||||
|
||||
class Panel(gtk.VBox):
|
||||
|
||||
__gtype_name__ = 'SugarPanel'
|
||||
|
||||
def __init__(self):
|
||||
gtk.VBox.__init__(self)
|
||||
@@ -0,0 +1,104 @@
|
||||
# Copyright (C) 2009, Aleksey Lim
|
||||
#
|
||||
# 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.toolbutton import ToolButton
|
||||
from sugar.graphics.palette import Palette
|
||||
|
||||
|
||||
class RadioMenuButton(ToolButton):
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
ToolButton.__init__(self, **kwargs)
|
||||
self.selected_button = None
|
||||
|
||||
if self.props.palette:
|
||||
self.__palette_cb(None, None)
|
||||
|
||||
self.connect('notify::palette', self.__palette_cb)
|
||||
|
||||
def __palette_cb(self, widget, pspec):
|
||||
if not isinstance(self.props.palette, RadioPalette):
|
||||
return
|
||||
self.props.palette.update_button()
|
||||
|
||||
def do_clicked(self):
|
||||
if self.palette is None:
|
||||
return
|
||||
if self.palette.is_up() and \
|
||||
self.palette.palette_state == Palette.SECONDARY:
|
||||
self.palette.popdown(immediate=True)
|
||||
else:
|
||||
self.palette.popup(immediate=True, state=Palette.SECONDARY)
|
||||
|
||||
|
||||
class RadioToolsButton(RadioMenuButton):
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
RadioMenuButton.__init__(self, **kwargs)
|
||||
|
||||
def do_clicked(self):
|
||||
if not self.selected_button:
|
||||
return
|
||||
self.selected_button.emit('clicked')
|
||||
|
||||
|
||||
class RadioPalette(Palette):
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
Palette.__init__(self, **kwargs)
|
||||
|
||||
self.button_box = gtk.HBox()
|
||||
self.button_box.show()
|
||||
self.set_content(self.button_box)
|
||||
|
||||
def append(self, button, label):
|
||||
children = self.button_box.get_children()
|
||||
|
||||
if button.palette is not None:
|
||||
raise RuntimeError("Palette's button should not have sub-palettes")
|
||||
|
||||
button.show()
|
||||
button.connect('clicked', self.__clicked_cb)
|
||||
self.button_box.pack_start(button, fill=False)
|
||||
button.palette_label = label
|
||||
|
||||
if not children:
|
||||
self.__clicked_cb(button)
|
||||
|
||||
def update_button(self):
|
||||
for i in self.button_box.get_children():
|
||||
self.__clicked_cb(i)
|
||||
|
||||
def __clicked_cb(self, button):
|
||||
if not button.get_active():
|
||||
return
|
||||
|
||||
self.set_primary_text(button.palette_label)
|
||||
self.popdown(immediate=True)
|
||||
|
||||
if self.invoker is not None:
|
||||
parent = self.invoker.parent
|
||||
else:
|
||||
parent = None
|
||||
if not isinstance(parent, RadioMenuButton):
|
||||
return
|
||||
|
||||
parent.props.label = button.palette_label
|
||||
parent.set_icon(button.props.icon_name)
|
||||
parent.selected_button = button
|
||||
@@ -0,0 +1,182 @@
|
||||
# Copyright (C) 2007, Red Hat, Inc.
|
||||
# Copyright (C) 2007-2008, 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.
|
||||
|
||||
"""
|
||||
STABLE.
|
||||
"""
|
||||
|
||||
import gtk
|
||||
import gobject
|
||||
|
||||
from sugar.graphics.icon import Icon
|
||||
from sugar.graphics.palette import Palette, ToolInvoker
|
||||
from sugar.graphics import toolbutton
|
||||
|
||||
|
||||
class RadioToolButton(gtk.RadioToolButton):
|
||||
"""
|
||||
An implementation of a "push" button.
|
||||
|
||||
"""
|
||||
|
||||
__gtype_name__ = 'SugarRadioToolButton'
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self._accelerator = None
|
||||
self._tooltip = None
|
||||
self._xo_color = None
|
||||
self._palette_invoker = ToolInvoker()
|
||||
|
||||
gobject.GObject.__init__(self, **kwargs)
|
||||
|
||||
self._palette_invoker.attach_tool(self)
|
||||
|
||||
self.connect('destroy', self.__destroy_cb)
|
||||
|
||||
def __destroy_cb(self, icon):
|
||||
if self._palette_invoker is not None:
|
||||
self._palette_invoker.detach()
|
||||
|
||||
def set_tooltip(self, tooltip):
|
||||
"""
|
||||
Set a simple palette with just a single label.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
tooltip:
|
||||
|
||||
Returns
|
||||
-------
|
||||
None
|
||||
|
||||
"""
|
||||
if self.palette is None or self._tooltip is None:
|
||||
self.palette = Palette(tooltip)
|
||||
elif self.palette is not None:
|
||||
self.palette.set_primary_text(tooltip)
|
||||
|
||||
self._tooltip = tooltip
|
||||
|
||||
# Set label, shows up when toolbar overflows
|
||||
gtk.RadioToolButton.set_label(self, tooltip)
|
||||
|
||||
def get_tooltip(self):
|
||||
return self._tooltip
|
||||
|
||||
tooltip = gobject.property(type=str, setter=set_tooltip,
|
||||
getter=get_tooltip)
|
||||
|
||||
def set_accelerator(self, accelerator):
|
||||
"""
|
||||
Sets the accelerator.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
accelerator:
|
||||
|
||||
Returns
|
||||
-------
|
||||
None
|
||||
|
||||
"""
|
||||
self._accelerator = accelerator
|
||||
toolbutton.setup_accelerator(self)
|
||||
|
||||
def get_accelerator(self):
|
||||
"""
|
||||
Returns the accelerator for the button.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
None
|
||||
|
||||
Returns
|
||||
------
|
||||
accelerator:
|
||||
|
||||
"""
|
||||
return self._accelerator
|
||||
|
||||
accelerator = gobject.property(type=str, setter=set_accelerator,
|
||||
getter=get_accelerator)
|
||||
|
||||
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_named_icon(self):
|
||||
if self.props.icon_widget is not None:
|
||||
return self.props.icon_widget.props.icon_name
|
||||
else:
|
||||
return None
|
||||
|
||||
named_icon = gobject.property(type=str, setter=set_named_icon,
|
||||
getter=get_named_icon)
|
||||
|
||||
def set_xo_color(self, xo_color):
|
||||
if self._xo_color != xo_color:
|
||||
self._xo_color = xo_color
|
||||
if self.props.icon_widget is not None:
|
||||
self.props.icon_widget.props.xo_color = xo_color
|
||||
|
||||
def get_xo_color(self):
|
||||
return self._xo_color
|
||||
|
||||
xo_color = gobject.property(type=object, setter=set_xo_color,
|
||||
getter=get_xo_color)
|
||||
|
||||
def create_palette(self):
|
||||
return None
|
||||
|
||||
def get_palette(self):
|
||||
return self._palette_invoker.palette
|
||||
|
||||
def set_palette(self, palette):
|
||||
self._palette_invoker.palette = palette
|
||||
|
||||
palette = gobject.property(
|
||||
type=object, setter=set_palette, getter=get_palette)
|
||||
|
||||
def get_palette_invoker(self):
|
||||
return self._palette_invoker
|
||||
|
||||
def set_palette_invoker(self, palette_invoker):
|
||||
self._palette_invoker.detach()
|
||||
self._palette_invoker = palette_invoker
|
||||
|
||||
palette_invoker = gobject.property(
|
||||
type=object, setter=set_palette_invoker, getter=get_palette_invoker)
|
||||
|
||||
def do_expose_event(self, event):
|
||||
child = self.get_child()
|
||||
allocation = self.get_allocation()
|
||||
|
||||
if self.palette and self.palette.is_up():
|
||||
invoker = self.palette.props.invoker
|
||||
invoker.draw_rectangle(event, self.palette)
|
||||
elif child.state == gtk.STATE_PRELIGHT:
|
||||
child.style.paint_box(event.window, gtk.STATE_PRELIGHT,
|
||||
gtk.SHADOW_NONE, event.area,
|
||||
child, 'toolbutton-prelight',
|
||||
allocation.x, allocation.y,
|
||||
allocation.width, allocation.height)
|
||||
|
||||
gtk.RadioToolButton.do_expose_event(self, event)
|
||||
@@ -0,0 +1,71 @@
|
||||
# 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.
|
||||
|
||||
"""
|
||||
STABLE.
|
||||
"""
|
||||
|
||||
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 radius 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,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.
|
||||
|
||||
STABLE.
|
||||
"""
|
||||
|
||||
import os
|
||||
import logging
|
||||
|
||||
import gtk
|
||||
import pango
|
||||
import gconf
|
||||
|
||||
|
||||
FOCUS_LINE_WIDTH = 2
|
||||
_TAB_CURVATURE = 1
|
||||
|
||||
|
||||
def _compute_zoom_factor():
|
||||
try:
|
||||
scaling = int(os.environ.get('SUGAR_SCALING', '100'))
|
||||
return scaling / 100.0
|
||||
except ValueError:
|
||||
logging.error('Invalid SUGAR_SCALING.')
|
||||
|
||||
return 1.0
|
||||
|
||||
|
||||
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(15)
|
||||
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)
|
||||
|
||||
client = gconf.client_get_default()
|
||||
FONT_SIZE = client.get_float('/desktop/sugar/font/default_size')
|
||||
FONT_FACE = client.get_string('/desktop/sugar/font/default_face')
|
||||
|
||||
FONT_NORMAL = Font('%s %f' % (FONT_FACE, FONT_SIZE))
|
||||
FONT_BOLD = Font('%s bold %f' % (FONT_FACE, FONT_SIZE))
|
||||
FONT_NORMAL_H = zoom(24)
|
||||
FONT_BOLD_H = zoom(24)
|
||||
|
||||
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('#282828')
|
||||
COLOR_BUTTON_GREY = Color('#808080')
|
||||
COLOR_INACTIVE_FILL = Color('#9D9FA1')
|
||||
COLOR_INACTIVE_STROKE = Color('#757575')
|
||||
COLOR_TEXT_FIELD_GREY = Color('#E5E5E5')
|
||||
COLOR_HIGHLIGHT = Color('#E7E7E7')
|
||||
|
||||
PALETTE_CURSOR_DISTANCE = zoom(10)
|
||||
|
||||
TOOLBAR_ARROW_SIZE = zoom(24)
|
||||
@@ -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.
|
||||
|
||||
"""
|
||||
STABLE.
|
||||
"""
|
||||
|
||||
import gobject
|
||||
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_invoker = ToolInvoker(self)
|
||||
self.set_named_icon(named_icon)
|
||||
|
||||
self.connect('destroy', self.__destroy_cb)
|
||||
|
||||
def __destroy_cb(self, icon):
|
||||
if self._palette_invoker is not None:
|
||||
self._palette_invoker.detach()
|
||||
|
||||
def set_named_icon(self, named_icon):
|
||||
icon = Icon(icon_name=named_icon)
|
||||
self.set_icon_widget(icon)
|
||||
icon.show()
|
||||
|
||||
def create_palette(self):
|
||||
return None
|
||||
|
||||
def get_palette(self):
|
||||
return self._palette_invoker.palette
|
||||
|
||||
def set_palette(self, palette):
|
||||
self._palette_invoker.palette = palette
|
||||
|
||||
palette = gobject.property(
|
||||
type=object, setter=set_palette, getter=get_palette)
|
||||
|
||||
def get_palette_invoker(self):
|
||||
return self._palette_invoker
|
||||
|
||||
def set_palette_invoker(self, palette_invoker):
|
||||
self._palette_invoker.detach()
|
||||
self._palette_invoker = palette_invoker
|
||||
|
||||
palette_invoker = gobject.property(
|
||||
type=object, setter=set_palette_invoker, getter=get_palette_invoker)
|
||||
|
||||
def set_tooltip(self, text):
|
||||
self.set_palette(Palette(text))
|
||||
|
||||
def do_expose_event(self, event):
|
||||
allocation = self.get_allocation()
|
||||
child = self.get_child()
|
||||
|
||||
if self.palette and self.palette.is_up():
|
||||
invoker = self.palette.props.invoker
|
||||
invoker.draw_rectangle(event, self.palette)
|
||||
elif child.state == gtk.STATE_PRELIGHT:
|
||||
child.style.paint_box(event.window, gtk.STATE_PRELIGHT,
|
||||
gtk.SHADOW_NONE, event.area,
|
||||
child, 'toolbutton-prelight',
|
||||
allocation.x, allocation.y,
|
||||
allocation.width, allocation.height)
|
||||
|
||||
gtk.ToggleToolButton.do_expose_event(self, event)
|
||||
|
||||
palette = property(get_palette, set_palette)
|
||||
@@ -0,0 +1,332 @@
|
||||
# Copyright (C) 2009, Aleksey Lim
|
||||
#
|
||||
# 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 import style
|
||||
from sugar.graphics.palette import PaletteWindow, ToolInvoker
|
||||
from sugar.graphics.toolbutton import ToolButton
|
||||
from sugar.graphics import palettegroup
|
||||
|
||||
|
||||
class ToolbarButton(ToolButton):
|
||||
|
||||
def __init__(self, page=None, **kwargs):
|
||||
ToolButton.__init__(self, **kwargs)
|
||||
|
||||
self.page_widget = None
|
||||
|
||||
self.set_page(page)
|
||||
|
||||
self.connect('clicked',
|
||||
lambda widget: self.set_expanded(not self.is_expanded()))
|
||||
|
||||
self.connect('hierarchy-changed', self.__hierarchy_changed_cb)
|
||||
|
||||
def __hierarchy_changed_cb(self, tool_button, previous_toplevel):
|
||||
if hasattr(self.parent, 'owner'):
|
||||
if self.page_widget and previous_toplevel is None:
|
||||
self._unparent()
|
||||
self.parent.owner.pack_start(self.page_widget)
|
||||
self.set_expanded(False)
|
||||
|
||||
def get_toolbar_box(self):
|
||||
if not hasattr(self.parent, 'owner'):
|
||||
return None
|
||||
return self.parent.owner
|
||||
|
||||
toolbar_box = property(get_toolbar_box)
|
||||
|
||||
def get_page(self):
|
||||
if self.page_widget is None:
|
||||
return None
|
||||
return _get_embedded_page(self.page_widget)
|
||||
|
||||
def set_page(self, page):
|
||||
if page is None:
|
||||
self.page_widget = None
|
||||
return
|
||||
self.page_widget, alignment_ = _embed_page(_Box, page)
|
||||
self.page_widget.set_size_request(-1, style.GRID_CELL_SIZE)
|
||||
page.show()
|
||||
if self.props.palette is None:
|
||||
self.props.palette = _ToolbarPalette(invoker=ToolInvoker(self))
|
||||
self._move_page_to_palette()
|
||||
|
||||
page = gobject.property(type=object, getter=get_page, setter=set_page)
|
||||
|
||||
def is_in_palette(self):
|
||||
return self.page is not None and \
|
||||
self.page_widget.parent == self.props.palette
|
||||
|
||||
def is_expanded(self):
|
||||
return self.page is not None and \
|
||||
not self.is_in_palette()
|
||||
|
||||
def popdown(self):
|
||||
if self.props.palette is not None:
|
||||
self.props.palette.popdown(immediate=True)
|
||||
|
||||
def set_expanded(self, expanded):
|
||||
self.popdown()
|
||||
|
||||
if self.page is None or self.is_expanded() == expanded:
|
||||
return
|
||||
|
||||
if not expanded:
|
||||
self._move_page_to_palette()
|
||||
return
|
||||
|
||||
box = self.toolbar_box
|
||||
|
||||
if box.expanded_button is not None:
|
||||
if box.expanded_button.window is not None:
|
||||
# need to redraw it to erase arrow
|
||||
box.expanded_button.window.invalidate_rect(None, True)
|
||||
box.expanded_button.set_expanded(False)
|
||||
box.expanded_button = self
|
||||
|
||||
self._unparent()
|
||||
|
||||
self.modify_bg(gtk.STATE_NORMAL, box.background)
|
||||
_setup_page(self.page_widget, box.background, box.props.padding)
|
||||
box.pack_start(self.page_widget)
|
||||
|
||||
def _move_page_to_palette(self):
|
||||
if self.is_in_palette():
|
||||
return
|
||||
|
||||
self._unparent()
|
||||
|
||||
if isinstance(self.props.palette, _ToolbarPalette):
|
||||
self.props.palette.add(self.page_widget)
|
||||
|
||||
def _unparent(self):
|
||||
if self.page_widget.parent is None:
|
||||
return
|
||||
self.page_widget.parent.remove(self.page_widget)
|
||||
|
||||
def do_expose_event(self, event):
|
||||
if not self.is_expanded() or self.props.palette is not None and \
|
||||
self.props.palette.is_up():
|
||||
ToolButton.do_expose_event(self, event)
|
||||
_paint_arrow(self, event, gtk.ARROW_DOWN)
|
||||
return
|
||||
|
||||
alloc = self.allocation
|
||||
|
||||
self.get_style().paint_box(event.window,
|
||||
gtk.STATE_NORMAL, gtk.SHADOW_IN, event.area, self,
|
||||
'palette-invoker', alloc.x, 0,
|
||||
alloc.width, alloc.height + style.FOCUS_LINE_WIDTH)
|
||||
|
||||
if self.child.state != gtk.STATE_PRELIGHT:
|
||||
self.get_style().paint_box(event.window,
|
||||
gtk.STATE_NORMAL, gtk.SHADOW_NONE, event.area, self, None,
|
||||
alloc.x + style.FOCUS_LINE_WIDTH, style.FOCUS_LINE_WIDTH,
|
||||
alloc.width - style.FOCUS_LINE_WIDTH * 2, alloc.height)
|
||||
|
||||
gtk.ToolButton.do_expose_event(self, event)
|
||||
_paint_arrow(self, event, gtk.ARROW_UP)
|
||||
|
||||
|
||||
class ToolbarBox(gtk.VBox):
|
||||
|
||||
def __init__(self, padding=style.TOOLBOX_HORIZONTAL_PADDING):
|
||||
gtk.VBox.__init__(self)
|
||||
self._expanded_button_index = -1
|
||||
self.background = None
|
||||
|
||||
self._toolbar = gtk.Toolbar()
|
||||
self._toolbar.owner = self
|
||||
self._toolbar.connect('remove', self.__remove_cb)
|
||||
|
||||
self._toolbar_widget, self._toolbar_alignment = \
|
||||
_embed_page(gtk.EventBox, self._toolbar)
|
||||
self.pack_start(self._toolbar_widget)
|
||||
|
||||
self.props.padding = padding
|
||||
self.modify_bg(gtk.STATE_NORMAL,
|
||||
style.COLOR_TOOLBAR_GREY.get_gdk_color())
|
||||
|
||||
def get_toolbar(self):
|
||||
return self._toolbar
|
||||
|
||||
toolbar = property(get_toolbar)
|
||||
|
||||
def get_expanded_button(self):
|
||||
if self._expanded_button_index == -1:
|
||||
return None
|
||||
return self.toolbar.get_nth_item(self._expanded_button_index)
|
||||
|
||||
def set_expanded_button(self, button):
|
||||
if not button in self.toolbar:
|
||||
self._expanded_button_index = -1
|
||||
return
|
||||
self._expanded_button_index = self.toolbar.get_item_index(button)
|
||||
|
||||
expanded_button = property(get_expanded_button, set_expanded_button)
|
||||
|
||||
def get_padding(self):
|
||||
return self._toolbar_alignment.props.left_padding
|
||||
|
||||
def set_padding(self, pad):
|
||||
self._toolbar_alignment.set_padding(0, 0, pad, pad)
|
||||
|
||||
padding = gobject.property(type=object,
|
||||
getter=get_padding, setter=set_padding)
|
||||
|
||||
def modify_bg(self, state, color):
|
||||
if state == gtk.STATE_NORMAL:
|
||||
self.background = color
|
||||
self._toolbar_widget.modify_bg(state, color)
|
||||
self.toolbar.modify_bg(state, color)
|
||||
|
||||
def __remove_cb(self, sender, button):
|
||||
if not isinstance(button, ToolbarButton):
|
||||
return
|
||||
button.popdown()
|
||||
if button == self.expanded_button:
|
||||
self.remove(button.page_widget)
|
||||
self._expanded_button_index = -1
|
||||
|
||||
|
||||
class _ToolbarPalette(PaletteWindow):
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
PaletteWindow.__init__(self, **kwargs)
|
||||
self.set_border_width(0)
|
||||
self._has_focus = False
|
||||
|
||||
group = palettegroup.get_group('default')
|
||||
group.connect('popdown', self.__group_popdown_cb)
|
||||
self.set_group_id('toolbarbox')
|
||||
|
||||
def get_expanded_button(self):
|
||||
return self.invoker.parent
|
||||
|
||||
expanded_button = property(get_expanded_button)
|
||||
|
||||
def on_invoker_enter(self):
|
||||
PaletteWindow.on_invoker_enter(self)
|
||||
self._set_focus(True)
|
||||
|
||||
def on_invoker_leave(self):
|
||||
PaletteWindow.on_invoker_leave(self)
|
||||
self._set_focus(False)
|
||||
|
||||
def on_enter(self, event):
|
||||
PaletteWindow.on_enter(self, event)
|
||||
self._set_focus(True)
|
||||
|
||||
def on_leave(self, event):
|
||||
PaletteWindow.on_enter(self, event)
|
||||
self._set_focus(False)
|
||||
|
||||
def _set_focus(self, new_focus):
|
||||
self._has_focus = new_focus
|
||||
if not self._has_focus:
|
||||
group = palettegroup.get_group('default')
|
||||
if not group.is_up():
|
||||
self.popdown()
|
||||
|
||||
def do_size_request(self, requisition):
|
||||
gtk.Window.do_size_request(self, requisition)
|
||||
requisition.width = max(requisition.width,
|
||||
gtk.gdk.screen_width())
|
||||
|
||||
def popup(self, immediate=False):
|
||||
button = self.expanded_button
|
||||
if button.is_expanded():
|
||||
return
|
||||
box = button.toolbar_box
|
||||
_setup_page(button.page_widget, style.COLOR_BLACK.get_gdk_color(),
|
||||
box.props.padding)
|
||||
PaletteWindow.popup(self, immediate)
|
||||
|
||||
def __group_popdown_cb(self, group):
|
||||
if not self._has_focus:
|
||||
self.popdown(immediate=True)
|
||||
|
||||
|
||||
class _Box(gtk.EventBox):
|
||||
|
||||
def __init__(self):
|
||||
gtk.EventBox.__init__(self)
|
||||
self.connect('expose-event', self.do_expose_event)
|
||||
self.set_app_paintable(True)
|
||||
|
||||
def do_expose_event(self, widget, event):
|
||||
if self.parent.expanded_button is None:
|
||||
return
|
||||
alloc = self.parent.expanded_button.allocation
|
||||
self.get_style().paint_box(event.window,
|
||||
gtk.STATE_NORMAL, gtk.SHADOW_IN, event.area, self,
|
||||
'palette-invoker', -style.FOCUS_LINE_WIDTH, 0,
|
||||
self.allocation.width + style.FOCUS_LINE_WIDTH * 2,
|
||||
self.allocation.height + style.FOCUS_LINE_WIDTH)
|
||||
self.get_style().paint_box(event.window,
|
||||
gtk.STATE_NORMAL, gtk.SHADOW_NONE, event.area, self, None,
|
||||
alloc.x + style.FOCUS_LINE_WIDTH, 0,
|
||||
alloc.width - style.FOCUS_LINE_WIDTH * 2,
|
||||
style.FOCUS_LINE_WIDTH)
|
||||
|
||||
|
||||
def _setup_page(page_widget, color, hpad):
|
||||
vpad = style.FOCUS_LINE_WIDTH
|
||||
page_widget.child.set_padding(vpad, vpad, hpad, hpad)
|
||||
|
||||
page = _get_embedded_page(page_widget)
|
||||
page.modify_bg(gtk.STATE_NORMAL, color)
|
||||
if isinstance(page, gtk.Container):
|
||||
for i in page.get_children():
|
||||
i.modify_bg(gtk.STATE_INSENSITIVE, color)
|
||||
|
||||
page_widget.modify_bg(gtk.STATE_NORMAL, color)
|
||||
page_widget.modify_bg(gtk.STATE_PRELIGHT, color)
|
||||
|
||||
|
||||
def _embed_page(box_class, page):
|
||||
page.show()
|
||||
|
||||
alignment = gtk.Alignment(0.0, 0.0, 1.0, 1.0)
|
||||
alignment.add(page)
|
||||
alignment.show()
|
||||
|
||||
page_widget = box_class()
|
||||
page_widget.modify_bg(gtk.STATE_ACTIVE,
|
||||
style.COLOR_BUTTON_GREY.get_gdk_color())
|
||||
page_widget.add(alignment)
|
||||
page_widget.show()
|
||||
|
||||
return (page_widget, alignment)
|
||||
|
||||
|
||||
def _get_embedded_page(page_widget):
|
||||
return page_widget.child.child
|
||||
|
||||
|
||||
def _paint_arrow(widget, event, arrow_type):
|
||||
alloc = widget.allocation
|
||||
x = alloc.x + alloc.width / 2 - style.TOOLBAR_ARROW_SIZE / 2
|
||||
y = alloc.y + alloc.height - int(style.TOOLBAR_ARROW_SIZE * .85)
|
||||
|
||||
widget.get_style().paint_arrow(event.window,
|
||||
gtk.STATE_NORMAL, gtk.SHADOW_NONE, event.area, widget,
|
||||
None, arrow_type, True,
|
||||
x, y, style.TOOLBAR_ARROW_SIZE, style.TOOLBAR_ARROW_SIZE)
|
||||
@@ -0,0 +1,96 @@
|
||||
# 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.
|
||||
|
||||
"""
|
||||
STABLE.
|
||||
"""
|
||||
|
||||
import gtk
|
||||
import gobject
|
||||
|
||||
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()
|
||||
|
||||
self._separator = gtk.HSeparator()
|
||||
self._separator.modify_bg(gtk.STATE_NORMAL,
|
||||
style.COLOR_PANEL_GREY.get_gdk_color())
|
||||
self._separator.set_size_request(1, style.TOOLBOX_SEPARATOR_HEIGHT)
|
||||
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)
|
||||
width, height_ = label.size_request()
|
||||
label.set_size_request(max(width, 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)
|
||||
|
||||
def get_current_toolbar(self):
|
||||
return self._notebook.get_current_page()
|
||||
|
||||
current_toolbar = property(get_current_toolbar, set_current_toolbar)
|
||||
@@ -0,0 +1,162 @@
|
||||
# Copyright (C) 2007, Red Hat, Inc.
|
||||
# Copyright (C) 2008, 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.
|
||||
|
||||
"""
|
||||
STABLE.
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
import gtk
|
||||
import gobject
|
||||
|
||||
from sugar.graphics.icon import Icon
|
||||
from sugar.graphics.palette import Palette, ToolInvoker
|
||||
|
||||
|
||||
def _add_accelerator(tool_button):
|
||||
if not tool_button.props.accelerator or not tool_button.get_toplevel() or \
|
||||
not tool_button.child:
|
||||
return
|
||||
|
||||
# TODO: should we remove the accelerator from the prev top level?
|
||||
|
||||
accel_group = tool_button.get_toplevel().get_data('sugar-accel-group')
|
||||
if not accel_group:
|
||||
logging.warning('No gtk.AccelGroup in the top level window.')
|
||||
return
|
||||
|
||||
keyval, mask = gtk.accelerator_parse(tool_button.props.accelerator)
|
||||
# the accelerator needs to be set at the child, so the gtk.AccelLabel
|
||||
# in the palette can pick it up.
|
||||
tool_button.child.add_accelerator('clicked', accel_group, keyval, mask,
|
||||
gtk.ACCEL_LOCKED | gtk.ACCEL_VISIBLE)
|
||||
|
||||
|
||||
def _hierarchy_changed_cb(tool_button, previous_toplevel):
|
||||
_add_accelerator(tool_button)
|
||||
|
||||
|
||||
def setup_accelerator(tool_button):
|
||||
_add_accelerator(tool_button)
|
||||
tool_button.connect('hierarchy-changed', _hierarchy_changed_cb)
|
||||
|
||||
|
||||
class ToolButton(gtk.ToolButton):
|
||||
|
||||
__gtype_name__ = 'SugarToolButton'
|
||||
|
||||
def __init__(self, icon_name=None, **kwargs):
|
||||
self._accelerator = None
|
||||
self._tooltip = None
|
||||
self._palette_invoker = ToolInvoker()
|
||||
|
||||
gobject.GObject.__init__(self, **kwargs)
|
||||
|
||||
self._palette_invoker.attach_tool(self)
|
||||
|
||||
if icon_name:
|
||||
self.set_icon(icon_name)
|
||||
|
||||
self.get_child().connect('can-activate-accel',
|
||||
self.__button_can_activate_accel_cb)
|
||||
|
||||
self.connect('destroy', self.__destroy_cb)
|
||||
|
||||
def __destroy_cb(self, icon):
|
||||
if self._palette_invoker is not None:
|
||||
self._palette_invoker.detach()
|
||||
|
||||
def __button_can_activate_accel_cb(self, button, signal_id):
|
||||
# Accept activation via accelerators regardless of this widget's state
|
||||
return True
|
||||
|
||||
def set_tooltip(self, tooltip):
|
||||
""" Set a simple palette with just a single label.
|
||||
"""
|
||||
if self.palette is None or self._tooltip is None:
|
||||
self.palette = Palette(tooltip)
|
||||
elif self.palette is not None:
|
||||
self.palette.set_primary_text(tooltip)
|
||||
|
||||
self._tooltip = tooltip
|
||||
|
||||
# Set label, shows up when toolbar overflows
|
||||
gtk.ToolButton.set_label(self, tooltip)
|
||||
|
||||
def get_tooltip(self):
|
||||
return self._tooltip
|
||||
|
||||
tooltip = gobject.property(type=str, setter=set_tooltip,
|
||||
getter=get_tooltip)
|
||||
|
||||
def set_accelerator(self, accelerator):
|
||||
self._accelerator = accelerator
|
||||
setup_accelerator(self)
|
||||
|
||||
def get_accelerator(self):
|
||||
return self._accelerator
|
||||
|
||||
accelerator = gobject.property(type=str, setter=set_accelerator,
|
||||
getter=get_accelerator)
|
||||
|
||||
def set_icon(self, icon_name):
|
||||
icon = Icon(icon_name=icon_name)
|
||||
self.set_icon_widget(icon)
|
||||
icon.show()
|
||||
|
||||
def create_palette(self):
|
||||
return None
|
||||
|
||||
def get_palette(self):
|
||||
return self._palette_invoker.palette
|
||||
|
||||
def set_palette(self, palette):
|
||||
self._palette_invoker.palette = palette
|
||||
|
||||
palette = gobject.property(
|
||||
type=object, setter=set_palette, getter=get_palette)
|
||||
|
||||
def get_palette_invoker(self):
|
||||
return self._palette_invoker
|
||||
|
||||
def set_palette_invoker(self, palette_invoker):
|
||||
self._palette_invoker.detach()
|
||||
self._palette_invoker = palette_invoker
|
||||
|
||||
palette_invoker = gobject.property(
|
||||
type=object, setter=set_palette_invoker, getter=get_palette_invoker)
|
||||
|
||||
def do_expose_event(self, event):
|
||||
child = self.get_child()
|
||||
allocation = self.get_allocation()
|
||||
if self.palette and self.palette.is_up():
|
||||
invoker = self.palette.props.invoker
|
||||
invoker.draw_rectangle(event, self.palette)
|
||||
elif child.state == gtk.STATE_PRELIGHT:
|
||||
child.style.paint_box(event.window, gtk.STATE_PRELIGHT,
|
||||
gtk.SHADOW_NONE, event.area,
|
||||
child, 'toolbutton-prelight',
|
||||
allocation.x, allocation.y,
|
||||
allocation.width, allocation.height)
|
||||
|
||||
gtk.ToolButton.do_expose_event(self, event)
|
||||
|
||||
def do_clicked(self):
|
||||
if self.palette:
|
||||
self.palette.popdown(True)
|
||||
@@ -0,0 +1,64 @@
|
||||
# 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.
|
||||
|
||||
"""
|
||||
STABLE.
|
||||
"""
|
||||
|
||||
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,468 @@
|
||||
# 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.
|
||||
|
||||
"""
|
||||
STABLE.
|
||||
"""
|
||||
|
||||
import gobject
|
||||
import gtk
|
||||
|
||||
from sugar.graphics import style
|
||||
from sugar.graphics.palette import ToolInvoker
|
||||
from sugar.graphics.toolbutton import ToolButton
|
||||
from sugar.graphics.icon import Icon
|
||||
|
||||
|
||||
_PREVIOUS_PAGE = 0
|
||||
_NEXT_PAGE = 1
|
||||
|
||||
|
||||
class _TrayViewport(gtk.Viewport):
|
||||
|
||||
__gproperties__ = {
|
||||
'scrollable': (bool, None, None, False, gobject.PARAM_READABLE),
|
||||
'can-scroll-prev': (bool, None, None, False, gobject.PARAM_READABLE),
|
||||
'can-scroll-next': (bool, None, None, False, gobject.PARAM_READABLE),
|
||||
}
|
||||
|
||||
def __init__(self, orientation):
|
||||
self.orientation = orientation
|
||||
self._scrollable = False
|
||||
self._can_scroll_next = False
|
||||
self._can_scroll_prev = 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)
|
||||
|
||||
if self.orientation == gtk.ORIENTATION_HORIZONTAL:
|
||||
adj = self.get_hadjustment()
|
||||
else:
|
||||
adj = self.get_vadjustment()
|
||||
adj.connect('changed', self._adjustment_changed_cb)
|
||||
adj.connect('value-changed', self._adjustment_changed_cb)
|
||||
|
||||
def scroll(self, direction):
|
||||
if direction == _PREVIOUS_PAGE:
|
||||
self._scroll_previous()
|
||||
elif direction == _NEXT_PAGE:
|
||||
self._scroll_next()
|
||||
|
||||
def scroll_to_item(self, item):
|
||||
"""This function scrolls the viewport so that item will be visible."""
|
||||
assert item in self.traybar.get_children()
|
||||
|
||||
# Get the allocation, and make sure that it is visible
|
||||
if self.orientation == gtk.ORIENTATION_HORIZONTAL:
|
||||
adj = self.get_hadjustment()
|
||||
start = item.allocation.x
|
||||
stop = item.allocation.x + item.allocation.width
|
||||
else:
|
||||
adj = self.get_vadjustment()
|
||||
start = item.allocation.y
|
||||
stop = item.allocation.y + item.allocation.height
|
||||
|
||||
if start < adj.value:
|
||||
adj.value = start
|
||||
elif stop > adj.value + adj.page_size:
|
||||
adj.value = stop - adj.page_size
|
||||
|
||||
def _scroll_next(self):
|
||||
allocation = self.get_allocation()
|
||||
if self.orientation == gtk.ORIENTATION_HORIZONTAL:
|
||||
adj = self.get_hadjustment()
|
||||
new_value = adj.value + allocation.width
|
||||
adj.value = min(new_value, adj.upper - allocation.width)
|
||||
else:
|
||||
adj = self.get_vadjustment()
|
||||
new_value = adj.value + allocation.height
|
||||
adj.value = min(new_value, adj.upper - allocation.height)
|
||||
|
||||
def _scroll_previous(self):
|
||||
allocation = self.get_allocation()
|
||||
if self.orientation == gtk.ORIENTATION_HORIZONTAL:
|
||||
adj = self.get_hadjustment()
|
||||
new_value = adj.value - allocation.width
|
||||
adj.value = max(adj.lower, new_value)
|
||||
else:
|
||||
adj = self.get_vadjustment()
|
||||
new_value = adj.value - allocation.height
|
||||
adj.value = max(adj.lower, new_value)
|
||||
|
||||
def do_size_request(self, requisition):
|
||||
child_requisition = self.get_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 == 'scrollable':
|
||||
return self._scrollable
|
||||
elif pspec.name == 'can-scroll-next':
|
||||
return self._can_scroll_next
|
||||
elif pspec.name == 'can-scroll-prev':
|
||||
return self._can_scroll_prev
|
||||
|
||||
def _size_allocate_cb(self, viewport, allocation):
|
||||
bar_requisition = self.traybar.get_child_requisition()
|
||||
if self.orientation == gtk.ORIENTATION_HORIZONTAL:
|
||||
scrollable = bar_requisition[0] > allocation.width
|
||||
else:
|
||||
scrollable = bar_requisition[1] > allocation.height
|
||||
|
||||
if scrollable != self._scrollable:
|
||||
self._scrollable = scrollable
|
||||
self.notify('scrollable')
|
||||
|
||||
def _adjustment_changed_cb(self, adjustment):
|
||||
if adjustment.value <= adjustment.lower:
|
||||
can_scroll_prev = False
|
||||
else:
|
||||
can_scroll_prev = True
|
||||
|
||||
if adjustment.value + adjustment.page_size >= adjustment.upper:
|
||||
can_scroll_next = False
|
||||
else:
|
||||
can_scroll_next = True
|
||||
|
||||
if can_scroll_prev != self._can_scroll_prev:
|
||||
self._can_scroll_prev = can_scroll_prev
|
||||
self.notify('can-scroll-prev')
|
||||
|
||||
if can_scroll_next != self._can_scroll_next:
|
||||
self._can_scroll_next = can_scroll_next
|
||||
self.notify('can-scroll-next')
|
||||
|
||||
|
||||
class _TrayScrollButton(ToolButton):
|
||||
|
||||
def __init__(self, icon_name, scroll_direction):
|
||||
ToolButton.__init__(self)
|
||||
self._viewport = None
|
||||
|
||||
self._scroll_direction = scroll_direction
|
||||
|
||||
self.set_size_request(style.GRID_CELL_SIZE, style.GRID_CELL_SIZE)
|
||||
|
||||
self.icon = Icon(icon_name=icon_name,
|
||||
icon_size=gtk.ICON_SIZE_SMALL_TOOLBAR)
|
||||
# The alignment is a hack to work around gtk.ToolButton code
|
||||
# that sets the icon_size when the icon_widget is a gtk.Image
|
||||
alignment = gtk.Alignment(0.5, 0.5)
|
||||
alignment.add(self.icon)
|
||||
self.set_icon_widget(alignment)
|
||||
alignment.show_all()
|
||||
|
||||
self.connect('clicked', self._clicked_cb)
|
||||
|
||||
def set_viewport(self, viewport):
|
||||
self._viewport = viewport
|
||||
self._viewport.connect('notify::scrollable',
|
||||
self._viewport_scrollable_changed_cb)
|
||||
|
||||
if self._scroll_direction == _PREVIOUS_PAGE:
|
||||
self._viewport.connect('notify::can-scroll-prev',
|
||||
self._viewport_can_scroll_dir_changed_cb)
|
||||
self.set_sensitive(self._viewport.props.can_scroll_prev)
|
||||
else:
|
||||
self._viewport.connect('notify::can-scroll-next',
|
||||
self._viewport_can_scroll_dir_changed_cb)
|
||||
self.set_sensitive(self._viewport.props.can_scroll_next)
|
||||
|
||||
def _viewport_scrollable_changed_cb(self, viewport, pspec):
|
||||
self.props.visible = self._viewport.props.scrollable
|
||||
|
||||
def _viewport_can_scroll_dir_changed_cb(self, viewport, pspec):
|
||||
if self._scroll_direction == _PREVIOUS_PAGE:
|
||||
sensitive = self._viewport.props.can_scroll_prev
|
||||
else:
|
||||
sensitive = self._viewport.props.can_scroll_next
|
||||
|
||||
self.set_sensitive(sensitive)
|
||||
|
||||
def _clicked_cb(self, button):
|
||||
self._viewport.scroll(self._scroll_direction)
|
||||
|
||||
viewport = property(fset=set_viewport)
|
||||
|
||||
|
||||
ALIGN_TO_START = 0
|
||||
ALIGN_TO_END = 1
|
||||
|
||||
|
||||
class HTray(gtk.HBox):
|
||||
|
||||
__gtype_name__ = 'SugarHTray'
|
||||
|
||||
__gproperties__ = {
|
||||
'align': (int, None, None, 0, 1, ALIGN_TO_START,
|
||||
gobject.PARAM_READWRITE | gobject.PARAM_CONSTRUCT_ONLY),
|
||||
'drag-active': (bool, None, None, False, gobject.PARAM_READWRITE),
|
||||
}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self._drag_active = False
|
||||
self.align = ALIGN_TO_START
|
||||
|
||||
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
|
||||
|
||||
if self.align == ALIGN_TO_END:
|
||||
spacer = gtk.SeparatorToolItem()
|
||||
spacer.set_size_request(0, 0)
|
||||
spacer.props.draw = False
|
||||
spacer.set_expand(True)
|
||||
self._viewport.traybar.insert(spacer, 0)
|
||||
spacer.show()
|
||||
|
||||
def do_set_property(self, pspec, value):
|
||||
if pspec.name == 'align':
|
||||
self.align = value
|
||||
elif pspec.name == 'drag-active':
|
||||
self._set_drag_active(value)
|
||||
else:
|
||||
raise AssertionError
|
||||
|
||||
def do_get_property(self, pspec):
|
||||
if pspec.name == 'align':
|
||||
return self.align
|
||||
elif pspec.name == 'drag-active':
|
||||
return self._drag_active
|
||||
else:
|
||||
raise AssertionError
|
||||
|
||||
def _set_drag_active(self, active):
|
||||
if self._drag_active != active:
|
||||
self._drag_active = active
|
||||
if self._drag_active:
|
||||
self._viewport.traybar.modify_bg(gtk.STATE_NORMAL,
|
||||
style.COLOR_BLACK.get_gdk_color())
|
||||
else:
|
||||
self._viewport.traybar.modify_bg(gtk.STATE_NORMAL, None)
|
||||
|
||||
def get_children(self):
|
||||
children = self._viewport.traybar.get_children()[:]
|
||||
if self.align == ALIGN_TO_END:
|
||||
children = children[1:]
|
||||
return children
|
||||
|
||||
def add_item(self, item, index=-1):
|
||||
if self.align == ALIGN_TO_END and index > -1:
|
||||
index += 1
|
||||
self._viewport.traybar.insert(item, index)
|
||||
|
||||
def remove_item(self, item):
|
||||
self._viewport.traybar.remove(item)
|
||||
|
||||
def get_item_index(self, item):
|
||||
index = self._viewport.traybar.get_item_index(item)
|
||||
if self.align == ALIGN_TO_END:
|
||||
index -= 1
|
||||
return index
|
||||
|
||||
def scroll_to_item(self, item):
|
||||
self._viewport.scroll_to_item(item)
|
||||
|
||||
|
||||
class VTray(gtk.VBox):
|
||||
|
||||
__gtype_name__ = 'SugarVTray'
|
||||
|
||||
__gproperties__ = {
|
||||
'align': (int, None, None, 0, 1, ALIGN_TO_START,
|
||||
gobject.PARAM_READWRITE | gobject.PARAM_CONSTRUCT_ONLY),
|
||||
'drag-active': (bool, None, None, False, gobject.PARAM_READWRITE),
|
||||
}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self._drag_active = False
|
||||
self.align = ALIGN_TO_START
|
||||
|
||||
gobject.GObject.__init__(self, **kwargs)
|
||||
|
||||
scroll_up = _TrayScrollButton('go-up', _PREVIOUS_PAGE)
|
||||
self.pack_start(scroll_up, False)
|
||||
|
||||
self._viewport = _TrayViewport(gtk.ORIENTATION_VERTICAL)
|
||||
self.pack_start(self._viewport)
|
||||
self._viewport.show()
|
||||
|
||||
scroll_down = _TrayScrollButton('go-down', _NEXT_PAGE)
|
||||
self.pack_start(scroll_down, False)
|
||||
|
||||
scroll_up.viewport = self._viewport
|
||||
scroll_down.viewport = self._viewport
|
||||
|
||||
if self.align == ALIGN_TO_END:
|
||||
spacer = gtk.SeparatorToolItem()
|
||||
spacer.set_size_request(0, 0)
|
||||
spacer.props.draw = False
|
||||
spacer.set_expand(True)
|
||||
self._viewport.traybar.insert(spacer, 0)
|
||||
spacer.show()
|
||||
|
||||
def do_set_property(self, pspec, value):
|
||||
if pspec.name == 'align':
|
||||
self.align = value
|
||||
elif pspec.name == 'drag-active':
|
||||
self._set_drag_active(value)
|
||||
else:
|
||||
raise AssertionError
|
||||
|
||||
def do_get_property(self, pspec):
|
||||
if pspec.name == 'align':
|
||||
return self.align
|
||||
elif pspec.name == 'drag-active':
|
||||
return self._drag_active
|
||||
else:
|
||||
raise AssertionError
|
||||
|
||||
def _set_drag_active(self, active):
|
||||
if self._drag_active != active:
|
||||
self._drag_active = active
|
||||
if self._drag_active:
|
||||
self._viewport.traybar.modify_bg(gtk.STATE_NORMAL,
|
||||
style.COLOR_BLACK.get_gdk_color())
|
||||
else:
|
||||
self._viewport.traybar.modify_bg(gtk.STATE_NORMAL, None)
|
||||
|
||||
def get_children(self):
|
||||
children = self._viewport.traybar.get_children()[:]
|
||||
if self.align == ALIGN_TO_END:
|
||||
children = children[1:]
|
||||
return children
|
||||
|
||||
def add_item(self, item, index=-1):
|
||||
if self.align == ALIGN_TO_END and index > -1:
|
||||
index += 1
|
||||
self._viewport.traybar.insert(item, index)
|
||||
|
||||
def remove_item(self, item):
|
||||
self._viewport.traybar.remove(item)
|
||||
|
||||
def get_item_index(self, item):
|
||||
index = self._viewport.traybar.get_item_index(item)
|
||||
if self.align == ALIGN_TO_END:
|
||||
index -= 1
|
||||
return index
|
||||
|
||||
def scroll_to_item(self, item):
|
||||
self._viewport.scroll_to_item(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.set_app_paintable(True)
|
||||
|
||||
self._icon = Icon(icon_name=icon_name, xo_color=xo_color,
|
||||
icon_size=gtk.ICON_SIZE_LARGE_TOOLBAR)
|
||||
self.add(self._icon)
|
||||
self._icon.show()
|
||||
|
||||
def do_expose_event(self, event):
|
||||
palette = self.parent.palette
|
||||
if palette and palette.is_up():
|
||||
invoker = palette.props.invoker
|
||||
invoker.draw_rectangle(event, palette)
|
||||
|
||||
gtk.EventBox.do_expose_event(self, event)
|
||||
|
||||
def get_icon(self):
|
||||
return self._icon
|
||||
|
||||
|
||||
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._palette_invoker = ToolInvoker(self)
|
||||
|
||||
self.set_size_request(style.GRID_CELL_SIZE, style.GRID_CELL_SIZE)
|
||||
|
||||
self.connect('destroy', self.__destroy_cb)
|
||||
|
||||
def __destroy_cb(self, icon):
|
||||
if self._palette_invoker is not None:
|
||||
self._palette_invoker.detach()
|
||||
|
||||
def create_palette(self):
|
||||
return None
|
||||
|
||||
def get_palette(self):
|
||||
return self._palette_invoker.palette
|
||||
|
||||
def set_palette(self, palette):
|
||||
self._palette_invoker.palette = palette
|
||||
|
||||
palette = gobject.property(
|
||||
type=object, setter=set_palette, getter=get_palette)
|
||||
|
||||
def get_palette_invoker(self):
|
||||
return self._palette_invoker
|
||||
|
||||
def set_palette_invoker(self, palette_invoker):
|
||||
self._palette_invoker.detach()
|
||||
self._palette_invoker = palette_invoker
|
||||
|
||||
palette_invoker = gobject.property(
|
||||
type=object, setter=set_palette_invoker, getter=get_palette_invoker)
|
||||
|
||||
def get_icon(self):
|
||||
return self._icon_widget.get_icon()
|
||||
icon = property(get_icon, None)
|
||||
@@ -0,0 +1,297 @@
|
||||
# Copyright (C) 2007, Red Hat, Inc.
|
||||
# Copyright (C) 2009, Aleksey Lim, Sayamindu Dasgupta
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""
|
||||
STABLE.
|
||||
"""
|
||||
|
||||
import gobject
|
||||
import gtk
|
||||
import warnings
|
||||
|
||||
from sugar.graphics.icon import Icon
|
||||
from sugar.graphics import palettegroup
|
||||
|
||||
|
||||
_UNFULLSCREEN_BUTTON_VISIBILITY_TIMEOUT = 2
|
||||
|
||||
|
||||
class UnfullscreenButton(gtk.Window):
|
||||
|
||||
def __init__(self):
|
||||
gtk.Window.__init__(self)
|
||||
|
||||
self.set_decorated(False)
|
||||
self.set_resizable(False)
|
||||
self.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
|
||||
|
||||
self.set_border_width(0)
|
||||
|
||||
self.props.accept_focus = False
|
||||
|
||||
#Setup estimate of width, height
|
||||
w, h = gtk.icon_size_lookup(gtk.ICON_SIZE_LARGE_TOOLBAR)
|
||||
self._width = w
|
||||
self._height = h
|
||||
|
||||
self.connect('size-request', self._size_request_cb)
|
||||
|
||||
screen = self.get_screen()
|
||||
screen.connect('size-changed', self._screen_size_changed_cb)
|
||||
|
||||
self._button = gtk.Button()
|
||||
self._button.set_relief(gtk.RELIEF_NONE)
|
||||
|
||||
self._icon = Icon(icon_name='view-return',
|
||||
icon_size=gtk.ICON_SIZE_LARGE_TOOLBAR)
|
||||
self._icon.show()
|
||||
self._button.add(self._icon)
|
||||
|
||||
self._button.show()
|
||||
self.add(self._button)
|
||||
|
||||
def connect_button_press(self, cb):
|
||||
self._button.connect('button-press-event', cb)
|
||||
|
||||
def _reposition(self):
|
||||
x = gtk.gdk.screen_width() - self._width
|
||||
self.move(x, 0)
|
||||
|
||||
def _size_request_cb(self, widget, req):
|
||||
self._width = req.width
|
||||
self._height = req.height
|
||||
self._reposition()
|
||||
|
||||
def _screen_size_changed_cb(self, screen):
|
||||
self._reposition()
|
||||
|
||||
|
||||
class Window(gtk.Window):
|
||||
|
||||
def __init__(self, **args):
|
||||
self._enable_fullscreen_mode = True
|
||||
|
||||
gtk.Window.__init__(self, **args)
|
||||
|
||||
self.set_decorated(False)
|
||||
self.maximize()
|
||||
self.connect('realize', self.__window_realize_cb)
|
||||
self.connect('key-press-event', self.__key_press_cb)
|
||||
|
||||
self._toolbar_box = None
|
||||
self._alerts = []
|
||||
self._canvas = None
|
||||
self.tray = None
|
||||
|
||||
self.__vbox = gtk.VBox()
|
||||
self.__hbox = gtk.HBox()
|
||||
self.__vbox.pack_start(self.__hbox)
|
||||
self.__hbox.show()
|
||||
|
||||
self.add_events(gtk.gdk.POINTER_MOTION_HINT_MASK
|
||||
| gtk.gdk.POINTER_MOTION_MASK)
|
||||
self.connect('motion-notify-event', self.__motion_notify_cb)
|
||||
|
||||
self.add(self.__vbox)
|
||||
self.__vbox.show()
|
||||
|
||||
self._is_fullscreen = False
|
||||
self._unfullscreen_button = UnfullscreenButton()
|
||||
self._unfullscreen_button.set_transient_for(self)
|
||||
self._unfullscreen_button.connect_button_press(
|
||||
self.__unfullscreen_button_pressed)
|
||||
self._unfullscreen_button_timeout_id = None
|
||||
|
||||
def reveal(self):
|
||||
""" Make window active
|
||||
|
||||
In contrast with present(), brings window to the top
|
||||
even after invoking on response on non-gtk events.
|
||||
See #1423.
|
||||
"""
|
||||
if self.window is None:
|
||||
self.show()
|
||||
return
|
||||
timestamp = gtk.get_current_event_time()
|
||||
if not timestamp:
|
||||
timestamp = gtk.gdk.x11_get_server_time(self.window)
|
||||
self.window.focus(timestamp)
|
||||
|
||||
def fullscreen(self):
|
||||
palettegroup.popdown_all()
|
||||
if self._toolbar_box is not None:
|
||||
self._toolbar_box.hide()
|
||||
if self.tray is not None:
|
||||
self.tray.hide()
|
||||
|
||||
self._is_fullscreen = True
|
||||
|
||||
if self.props.enable_fullscreen_mode:
|
||||
self._unfullscreen_button.show()
|
||||
|
||||
if self._unfullscreen_button_timeout_id is not None:
|
||||
gobject.source_remove(self._unfullscreen_button_timeout_id)
|
||||
self._unfullscreen_button_timeout_id = None
|
||||
|
||||
self._unfullscreen_button_timeout_id = \
|
||||
gobject.timeout_add_seconds( \
|
||||
_UNFULLSCREEN_BUTTON_VISIBILITY_TIMEOUT, \
|
||||
self.__unfullscreen_button_timeout_cb)
|
||||
|
||||
def unfullscreen(self):
|
||||
if self._toolbar_box is not None:
|
||||
self._toolbar_box.show()
|
||||
if self.tray is not None:
|
||||
self.tray.show()
|
||||
|
||||
self._is_fullscreen = False
|
||||
|
||||
if self.props.enable_fullscreen_mode:
|
||||
self._unfullscreen_button.hide()
|
||||
|
||||
if self._unfullscreen_button_timeout_id:
|
||||
gobject.source_remove(self._unfullscreen_button_timeout_id)
|
||||
self._unfullscreen_button_timeout_id = None
|
||||
|
||||
def set_canvas(self, canvas):
|
||||
if self._canvas:
|
||||
self.__hbox.remove(self._canvas)
|
||||
|
||||
if canvas:
|
||||
self.__hbox.pack_start(canvas)
|
||||
|
||||
self._canvas = canvas
|
||||
self.__vbox.set_focus_child(self._canvas)
|
||||
|
||||
def get_canvas(self):
|
||||
return self._canvas
|
||||
|
||||
canvas = property(get_canvas, set_canvas)
|
||||
|
||||
def get_toolbar_box(self):
|
||||
return self._toolbar_box
|
||||
|
||||
def set_toolbar_box(self, toolbar_box):
|
||||
if self._toolbar_box:
|
||||
self.__vbox.remove(self._toolbar_box)
|
||||
|
||||
if toolbar_box:
|
||||
self.__vbox.pack_start(toolbar_box, False)
|
||||
self.__vbox.reorder_child(toolbar_box, 0)
|
||||
|
||||
self._toolbar_box = toolbar_box
|
||||
|
||||
toolbar_box = property(get_toolbar_box, set_toolbar_box)
|
||||
|
||||
def set_tray(self, tray, position):
|
||||
if self.tray:
|
||||
box = self.tray.get_parent()
|
||||
box.remove(self.tray)
|
||||
|
||||
if position == gtk.POS_LEFT:
|
||||
self.__hbox.pack_start(tray, False)
|
||||
elif position == gtk.POS_RIGHT:
|
||||
self.__hbox.pack_end(tray, False)
|
||||
elif position == gtk.POS_BOTTOM:
|
||||
self.__vbox.pack_end(tray, False)
|
||||
|
||||
self.tray = tray
|
||||
|
||||
def add_alert(self, alert):
|
||||
self._alerts.append(alert)
|
||||
if len(self._alerts) == 1:
|
||||
self.__vbox.pack_start(alert, False)
|
||||
if self._toolbar_box is not None:
|
||||
self.__vbox.reorder_child(alert, 1)
|
||||
else:
|
||||
self.__vbox.reorder_child(alert, 0)
|
||||
|
||||
def remove_alert(self, alert):
|
||||
if alert in self._alerts:
|
||||
self._alerts.remove(alert)
|
||||
# if the alert is the visible one on top of the queue
|
||||
if alert.get_parent() is not None:
|
||||
self.__vbox.remove(alert)
|
||||
if len(self._alerts) >= 1:
|
||||
self.__vbox.pack_start(self._alerts[0], False)
|
||||
if self._toolbar_box is not None:
|
||||
self.__vbox.reorder_child(self._alerts[0], 1)
|
||||
else:
|
||||
self.__vbox.reorder_child(self._alert[0], 0)
|
||||
|
||||
def __window_realize_cb(self, window):
|
||||
group = gtk.Window()
|
||||
group.realize()
|
||||
window.window.set_group(group.window)
|
||||
|
||||
def __key_press_cb(self, widget, event):
|
||||
key = gtk.gdk.keyval_name(event.keyval)
|
||||
if event.state & gtk.gdk.MOD1_MASK:
|
||||
if self.tray is not None and key == 'space':
|
||||
self.tray.props.visible = not self.tray.props.visible
|
||||
return True
|
||||
elif key == 'Escape' and self._is_fullscreen and \
|
||||
self.props.enable_fullscreen_mode:
|
||||
self.unfullscreen()
|
||||
return True
|
||||
return False
|
||||
|
||||
def __unfullscreen_button_pressed(self, widget, event):
|
||||
self.unfullscreen()
|
||||
|
||||
def __motion_notify_cb(self, widget, event):
|
||||
if self._is_fullscreen and self.props.enable_fullscreen_mode:
|
||||
if not self._unfullscreen_button.props.visible:
|
||||
self._unfullscreen_button.show()
|
||||
else:
|
||||
# Reset the timer
|
||||
if self._unfullscreen_button_timeout_id is not None:
|
||||
gobject.source_remove(self._unfullscreen_button_timeout_id)
|
||||
self._unfullscreen_button_timeout_id = None
|
||||
|
||||
self._unfullscreen_button_timeout_id = \
|
||||
gobject.timeout_add_seconds( \
|
||||
_UNFULLSCREEN_BUTTON_VISIBILITY_TIMEOUT, \
|
||||
self.__unfullscreen_button_timeout_cb)
|
||||
return False
|
||||
|
||||
def __unfullscreen_button_timeout_cb(self):
|
||||
self._unfullscreen_button.hide()
|
||||
self._unfullscreen_button_timeout_id = None
|
||||
return False
|
||||
|
||||
def set_enable_fullscreen_mode(self, enable_fullscreen_mode):
|
||||
self._enable_fullscreen_mode = enable_fullscreen_mode
|
||||
|
||||
def get_enable_fullscreen_mode(self):
|
||||
return self._enable_fullscreen_mode
|
||||
|
||||
enable_fullscreen_mode = gobject.property(type=object,
|
||||
setter=set_enable_fullscreen_mode, getter=get_enable_fullscreen_mode)
|
||||
|
||||
# DEPRECATED
|
||||
|
||||
def set_toolbox(self, toolbar_box):
|
||||
warnings.warn('use toolbar_box instead of toolbox', DeprecationWarning)
|
||||
self.set_toolbar_box(toolbar_box)
|
||||
|
||||
def get_toolbox(self):
|
||||
warnings.warn('use toolbar_box instead of toolbox', DeprecationWarning)
|
||||
return self._toolbar_box
|
||||
|
||||
toolbox = property(get_toolbox, set_toolbox)
|
||||
@@ -0,0 +1,282 @@
|
||||
# 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.
|
||||
|
||||
"""
|
||||
STABLE.
|
||||
"""
|
||||
|
||||
import random
|
||||
import logging
|
||||
|
||||
import gconf
|
||||
|
||||
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 not isinstance(color_string, (str, unicode)):
|
||||
logging.error('Invalid color string: %r', color_string)
|
||||
return None
|
||||
|
||||
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:
|
||||
randomize = True
|
||||
elif not is_valid(color_string):
|
||||
logging.debug('Color string is not valid: %s, '
|
||||
'fallback to default', color_string)
|
||||
client = gconf.client_get_default()
|
||||
color_string = client.get_string('/desktop/sugar/user/color')
|
||||
randomize = False
|
||||
else:
|
||||
randomize = False
|
||||
|
||||
if randomize:
|
||||
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