#
# Kiwi: a Framework and Enhanced Widgets for Python
#
# Copyright (C) 2005 Async Open Source
#
# 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.1 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
#
# Author(s): Lorenzo Gil Sanchez <lgs@sicem.biz>
# Johan Dahlin <jdahlin@async.com.br>
# Og B. Maciel <ogmaciel@src.gnome.org>
#
"""Graphical utilities: color management and eyecandy"""
import math
import StringIO
import cairo
import gobject
import gtk
from gtk import gdk
from kiwi.log import Logger
from kiwi.utils import gsignal, type_register
[docs]def gdk_color_to_string(color):
"""Convert a color to a #AABBCC string"""
return "#%02X%02X%02X" % (int(color.red) >> 8,
int(color.green) >> 8,
int(color.blue) >> 8)
[docs]def set_foreground(widget, color, state=gtk.STATE_NORMAL):
"""
Set the foreground color of a widget:
- widget: the widget we are changing the color
- color: a hexadecimal code or a well known color name
- state: the state we are afecting, see gtk.STATE_*
"""
widget.modify_fg(state, gdk.color_parse(color))
[docs]def get_foreground(widget, state=gtk.STATE_NORMAL):
"""Return the foreground color of the widget as a string"""
style = widget.get_style()
color = style.fg[state]
return gdk_color_to_string(color)
[docs]def set_background(widget, color, state=gtk.STATE_NORMAL):
"""
Set the background color of a widget:
- widget: the widget we are changing the color
- color: a hexadecimal code or a well known color name
- state: the state we are afecting, see gtk.STATE_*
"""
if isinstance(widget, gtk.Entry):
widget.modify_base(state, gdk.color_parse(color))
else:
widget.modify_bg(state, gdk.color_parse(color))
[docs]def get_background(widget, state=gtk.STATE_NORMAL):
"""Return the background color of the widget as a string"""
style = widget.get_style()
color = style.bg[state]
return gdk_color_to_string(color)
[docs]def quit_if_last(*args):
windows = [toplevel
for toplevel in gtk.window_list_toplevels()
if toplevel.get_property('type') == gtk.WINDOW_TOPLEVEL]
if len(windows) == 1:
gtk.main_quit()
def _select_notebook_tab(widget, event, notebook):
val = event.keyval - 48
if event.state & gdk.MOD1_MASK and 1 <= val <= 9:
notebook.set_current_page(val-1)
[docs]def register_notebook_shortcuts(dialog, notebook):
dialog.toplevel.connect('key-press-event', _select_notebook_tab, notebook)
[docs]class FadeOut(gobject.GObject):
"""I am a helper class to draw the fading effect of the background
Call my methods start() and stop() to control the fading.
"""
gsignal('done')
gsignal('color-changed', gdk.Color)
# How long time it'll take before we start (in ms)
COMPLAIN_DELAY = 500
MERGE_COLORS_DELAY = 100
ERROR_COLOR = "#ffd5d5"
def __init__(self, widget):
gobject.GObject.__init__(self)
self._widget = widget
self._start_color = None
self._background_timeout_id = -1
self._countdown_timeout_id = -1
self._log = Logger('fade')
self._done = False
def _merge_colors(self, src_color, dst_color, steps=10):
"""
Change the background of widget from src_color to dst_color
in the number of steps specified
"""
self._log.debug('_merge_colors: %s -> %s' % (src_color, dst_color))
if not src_color:
yield False
rs, gs, bs = src_color.red, src_color.green, src_color.blue
rd, gd, bd = dst_color.red, dst_color.green, dst_color.blue
rinc = (rd - rs) / float(steps)
ginc = (gd - gs) / float(steps)
binc = (bd - bs) / float(steps)
for dummy in xrange(steps):
rs += rinc
gs += ginc
bs += binc
col = gdk.color_parse("#%02X%02X%02X" % (int(rs) >> 8,
int(gs) >> 8,
int(bs) >> 8))
self.emit('color-changed', col)
yield True
self.emit('done')
self._background_timeout_id = -1
self._done = True
yield False
def _start_merging(self):
# If we changed during the delay
if self._background_timeout_id != -1:
self._log.debug('_start_merging: Already running')
return
self._log.debug('_start_merging: Starting')
func = self._merge_colors(self._start_color,
gdk.color_parse(FadeOut.ERROR_COLOR)).next
self._background_timeout_id = (
gobject.timeout_add(FadeOut.MERGE_COLORS_DELAY, func))
self._countdown_timeout_id = -1
[docs] def start(self, color):
"""Schedules a start of the countdown.
:param color: initial background color
:returns: True if we could start, False if was already in progress
"""
if self._background_timeout_id != -1:
self._log.debug('start: Background change already running')
return False
if self._countdown_timeout_id != -1:
self._log.debug('start: Countdown already running')
return False
if self._done:
self._log.debug('start: Not running, already set')
return False
self._start_color = color
self._log.debug('start: Scheduling')
self._countdown_timeout_id = gobject.timeout_add(
FadeOut.COMPLAIN_DELAY, self._start_merging)
return True
[docs] def stop(self):
"""Stops the fadeout and restores the background color"""
self._log.debug('Stopping')
if self._background_timeout_id != -1:
gobject.source_remove(self._background_timeout_id)
self._background_timeout_id = -1
if self._countdown_timeout_id != -1:
gobject.source_remove(self._countdown_timeout_id)
self._countdown_timeout_id = -1
self._widget.update_background(self._start_color)
self._done = False
type_register(FadeOut)
_pixbuf_cache = {}
# Based on code from BillReminder by Og Maciel and
# http://cairographics.org/cookbook/roundedrectangles/
[docs]def render_pixbuf(color_name, width=16, height=16, radius=4):
pixbuf = _pixbuf_cache.get(color_name)
if pixbuf is not None:
return pixbuf
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
cr = cairo.Context(surface)
if color_name:
pi2 = math.pi / 2
cr.new_sub_path()
cr.arc(radius, radius, radius, math.pi, 3 * pi2)
cr.arc(height - radius, radius, radius, 3 * pi2, 4 * pi2)
cr.arc(height - radius, width - radius, radius, 0, pi2)
cr.arc(radius, width - radius, radius, pi2, math.pi)
cr.close_path()
color = gtk.gdk.color_parse(color_name)
cr.set_source_rgb(
float(color.red) / 65536,
float(color.green) / 65536,
float(color.blue) / 65536)
else:
cr.set_source_rgba(0, 0, 0, 0.0)
cr.fill_preserve()
cr.set_source_rgba(0, 0, 0, 0.5)
cr.set_line_width(1)
cr.stroke()
buf = StringIO.StringIO()
surface.write_to_png(buf)
buf.seek(0)
loader = gtk.gdk.PixbufLoader()
loader.write(buf.getvalue())
loader.close()
pixbuf = loader.get_pixbuf()
_pixbuf_cache[color_name] = pixbuf
return pixbuf