# @Base: Miro - an RSS based video player application # Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011 # Participatory Culture Foundation # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # # In addition, as a special exception, the copyright holders give # permission to link the code of portions of this program with the OpenSSL # library. # # You must obey the GNU General Public License in all respects for all of # the code used other than OpenSSL. If you modify file(s) with this # exception, you may extend this exception to your version of the file(s), # but you are not obligated to do so. If you do not wish to do so, delete # this exception statement from your version. If you delete this exception # statement from all source files in the program, then also delete it here. """simple.py -- Collection of simple widgets.""" import gtk import gobject import pango from lvc.widgets import widgetconst from .base import Widget, Bin class Image(object): def __init__(self, path): try: self._set_pixbuf(gtk.gdk.pixbuf_new_from_file(path)) except gobject.GError, ge: raise ValueError("%s" % ge) self.width = self.pixbuf.get_width() self.height = self.pixbuf.get_height() def _set_pixbuf(self, pixbuf): self.pixbuf = pixbuf self.width = self.pixbuf.get_width() self.height = self.pixbuf.get_height() def resize(self, width, height): width = int(round(width)) height = int(round(height)) resized_pixbuf = self.pixbuf.scale_simple(width, height, gtk.gdk.INTERP_BILINEAR) return TransformedImage(resized_pixbuf) def resize_for_space(self, width, height): """Returns an image scaled to fit into the specified space at the correct height/width ratio. """ ratio = min(1.0 * width / self.width, 1.0 * height / self.height) return self.resize(ratio * self.width, ratio * self.height) def crop_and_scale(self, src_x, src_y, src_width, src_height, dest_width, dest_height): """Crop an image then scale it. The image will be cropped to the rectangle (src_x, src_y, src_width, src_height), that rectangle will be scaled to a new Image with tisez (dest_width, dest_height) """ dest = gtk.gdk.Pixbuf(self.pixbuf.get_colorspace(), self.pixbuf.get_has_alpha(), self.pixbuf.get_bits_per_sample(), dest_width, dest_height) scale_x = dest_width / float(src_width) scale_y = dest_height / float(src_height) self.pixbuf.scale(dest, 0, 0, dest_width, dest_height, -src_x * scale_x, -src_y * scale_y, scale_x, scale_y, gtk.gdk.INTERP_BILINEAR) return TransformedImage(dest) class TransformedImage(Image): def __init__(self, pixbuf): # XXX intentionally not calling direct super's __init__; we should do # this differently self._set_pixbuf(pixbuf) class ImageDisplay(Widget): def __init__(self, image=None): Widget.__init__(self) self.set_widget(gtk.Image()) self.set_image(image) def set_image(self, image): self.image = image if image is not None: self._widget.set_from_pixbuf(image.pixbuf) else: self._widget.clear() class AnimatedImageDisplay(Widget): def __init__(self, path): Widget.__init__(self) self.set_widget(gtk.Image()) self._animation = gtk.gdk.PixbufAnimation(path) # Set to animate before we are shown and stop animating after # we disappear. self._widget.connect('map', lambda w: self._set_animate(True)) self._widget.connect('unmap-event', lambda w, a: self._set_animate(False)) def _set_animate(self, enabled): if enabled: self._widget.set_from_animation(self._animation) else: self._widget.clear() class Label(Widget): """Widget that displays simple text.""" def __init__(self, text="", color=None): Widget.__init__(self) self.set_widget(gtk.Label()) if text: self.set_text(text) self.attr_list = pango.AttrList() self.font_description = self._widget.style.font_desc.copy() self.scale_factor = 1.0 if color is not None: self.set_color(color) self.wrapped_widget_connect('style-set', self.on_style_set) def set_bold(self, bold): if bold: weight = pango.WEIGHT_BOLD else: weight = pango.WEIGHT_NORMAL self.font_description.set_weight(weight) self.set_attr(pango.AttrFontDesc(self.font_description)) def set_size(self, size): if size == widgetconst.SIZE_NORMAL: self.scale_factor = 1 elif size == widgetconst.SIZE_SMALL: self.scale_factor = 0.75 else: self.scale_factor = size baseline = self._widget.style.font_desc.get_size() self.font_description.set_size(int(baseline * self.scale_factor)) self.set_attr(pango.AttrFontDesc(self.font_description)) def get_preferred_width(self): return self._widget.size_request()[0] def on_style_set(self, widget, old_style): self.set_size(self.scale_factor) def set_wrap(self, wrap): self._widget.set_line_wrap(wrap) def set_alignment(self, alignment): # default to left. gtkalignment = gtk.JUSTIFY_LEFT if alignment == widgetconst.TEXT_JUSTIFY_LEFT: gtkalignment = gtk.JUSTIFY_LEFT elif alignment == widgetconst.TEXT_JUSTIFY_RIGHT: gtkalignment = gtk.JUSTIFY_RIGHT elif alignment == widgetconst.TEXT_JUSTIFY_CENTER: gtkalignment = gtk.JUSTIFY_CENTER self._widget.set_justify(gtkalignment) def get_alignment(self): return self._widget.get_justify() def get_width(self): return self._widget.get_layout().get_pixel_size()[0] def set_text(self, text): self._widget.set_text(text) def get_text(self): return self._widget.get_text().decode('utf-8') def set_selectable(self, val): self._widget.set_selectable(val) def set_attr(self, attr): attr.end_index = 65535 self.attr_list.change(attr) self._widget.set_attributes(self.attr_list) def set_color(self, color): color_as_int = (int(65535 * c) for c in color) self.set_attr(pango.AttrForeground(*color_as_int)) def baseline(self): pango_context = self._widget.get_pango_context() metrics = pango_context.get_metrics(self.font_description) return pango.PIXELS(metrics.get_descent()) def hide(self): self._widget.hide() def show(self): self._widget.show() class Scroller(Bin): def __init__(self, horizontal, vertical): Bin.__init__(self) self.set_widget(gtk.ScrolledWindow()) if horizontal: h_policy = gtk.POLICY_AUTOMATIC else: h_policy = gtk.POLICY_NEVER if vertical: v_policy = gtk.POLICY_AUTOMATIC else: v_policy = gtk.POLICY_NEVER self._widget.set_policy(h_policy, v_policy) def set_has_borders(self, has_border): pass def set_background_color(self, color): pass def add_child_to_widget(self): if (isinstance(self.child._widget, gtk.TreeView) or isinstance(self.child._widget, gtk.TextView)): # child has native scroller self._widget.add(self.child._widget) else: self._widget.add_with_viewport(self.child._widget) self._widget.get_child().set_shadow_type(gtk.SHADOW_NONE) if isinstance(self.child._widget, gtk.TextView): self._widget.set_shadow_type(gtk.SHADOW_IN) else: self._widget.set_shadow_type(gtk.SHADOW_NONE) def prepare_for_dark_content(self): # this is just a hack for cocoa pass class SolidBackground(Bin): def __init__(self, color=None): Bin.__init__(self) self.set_widget(gtk.EventBox()) if color is not None: self.set_background_color(color) def set_background_color(self, color): self.modify_style('base', gtk.STATE_NORMAL, self.make_color(color)) self.modify_style('bg', gtk.STATE_NORMAL, self.make_color(color)) class Expander(Bin): def __init__(self, child=None): Bin.__init__(self) self.set_widget(gtk.Expander()) if child is not None: self.add(child) self.label = None # This is a complete hack. GTK expanders have a transparent # background most of the time, except when they are prelighted. So we # just set the background to white there because that's what should # happen in the item list. self.modify_style('bg', gtk.STATE_PRELIGHT, gtk.gdk.color_parse('white')) def set_spacing(self, spacing): self._widget.set_spacing(spacing) def set_label(self, widget): self.label = widget self._widget.set_label_widget(widget._widget) widget._widget.show() def set_expanded(self, expanded): self._widget.set_expanded(expanded) class ProgressBar(Widget): def __init__(self): Widget.__init__(self) self.set_widget(gtk.ProgressBar()) self._timer = None def set_progress(self, fraction): self._widget.set_fraction(fraction) def start_pulsing(self): if self._timer is None: self._timer = gobject.timeout_add(100, self._do_pulse) def stop_pulsing(self): if self._timer: gobject.source_remove(self._timer) self._timer = None def _do_pulse(self): self._widget.pulse() return True class HLine(Widget): """A horizontal separator. Not to be confused with HSeparator, which is is a DrawingArea, not a Widget. """ def __init__(self): Widget.__init__(self) self.set_widget(gtk.HSeparator())