Update of /cvsroot/freevo/freevo/WIP/Dischi
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv22502
Added Files:
main.py screen.py area.py
Log Message:
new screen design test code, read future_ideas
--- NEW FILE: area.py ---
# -*- coding: iso-8859-1 -*-
# -----------------------------------------------------------------------
# area.py - An area for the Freevo skin
# -----------------------------------------------------------------------
# $Id: area.py,v 1.1 2004/07/16 19:51:54 dischi Exp $
#
# Notes:
#
#
# This is the main class for all area.
#
# If you want to create a new Skin_Area, please keep my problems in mind:
#
#
# 1. Not all areas are visible at the same time, some areas may change
# the settings and others don't
# 2. The listing and the view area can overlap, at the next item the image
# may be gone
# 3. alpha layers are slow to blit on a non alpha surface.
# 4. the blue_round1 draws two alpha masks, one for the listing, one
# for the view area. They overlap, but the overlapping area
# shouldn't be an addition of the transparent value
# 5. If you drop an alpha layer on the screen, you can't get the original
# background back by making a reverse alpha layer.
#
# For more informations contact me ([EMAIL PROTECTED])
#
# -----------------------------------------------------------------------
# $Log: area.py,v $
# Revision 1.1 2004/07/16 19:51:54 dischi
# new screen design test code, read future_ideas
#
# Revision 1.41 2004/07/10 12:33:41 dischi
# header cleanup
#
# Revision 1.40 2004/04/25 12:38:22 dischi
# move idlebar image to background
#
# -----------------------------------------------------------------------
# Freevo - A Home Theater PC framework
# Copyright (C) 2002 Krister Lagerstrom, et al.
# Please see the file freevo/Docs/CREDITS for a complete list of authors.
#
# 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 MER-
# CHANTABILITY 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.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# -----------------------------------------------------------------------
import copy
import os
import pygame
import stat
import osd
import config
import util
import xml_skin
# Create the OSD object
osd = osd.get_singleton()
from screen import *
import screen
class Geometry:
"""
Simple object with x, y, with, height values
"""
def __init__(self, x, y, width, height):
self.x = x
self.y = y
self.width = width
self.height = height
class Skin_Area:
"""
the base call for all areas. Each child needs two functions:
def update_content_needed
def update_content
"""
def __init__(self, name, imagecachesize=5):
self.area_name = name
self.area_val = None
self.redraw = True
self.layout = None
self.name = name
self.screen = screen.get_singleton()
self.objects = SkinObjects()
self.imagecache = util.objectcache.ObjectCache(imagecachesize,
desc='%s_image' % self.name)
def update_content_needed(self):
"""
this area needs a content update
"""
return True
def update_content(self):
"""
there is no content in this area
"""
pass
def clear(self):
for o in self.objects.bgimages:
self.screen.remove('bg', o)
for o in self.objects.rectangles:
self.screen.remove('alpha', o)
for o in self.objects.images:
self.screen.remove('content', o)
for o in self.objects.text:
self.screen.remove('content', o)
self.objects = SkinObjects()
def draw(self, settings, obj, menu, display_style=0, widget_type='menu',
force_redraw=False):
"""
this is the main draw function. This function draws the background,
checks if redraws are needed and calls the two update functions
for the different types of areas
"""
self.display_style = display_style
self.xml_settings = settings
self.widget_type = widget_type
self.menuw = obj
if menu:
self.menu = menu
if self.menu.force_skin_layout != -1:
self.display_style = self.menu.force_skin_layout
if self.menu.viewitem:
self.viewitem = self.menu.viewitem
else:
self.viewitem = self.menu.selected
if self.menu.infoitem:
self.infoitem = self.menu.infoitem
else:
self.infoitem = self.menu.selected
item_type = self.menu.item_types
self.scan_for_text_view(self.menu)
else:
self.menu = obj
item_type = None
try:
self.viewitem = obj.selected
self.infoitem = obj.selected
except AttributeError:
self.viewitem = obj
self.infoitem = obj
self.redraw = force_redraw
area = self.area_val
if area:
visible = area.visible
else:
visible = False
self.redraw = self.init_vars(settings, item_type, widget_type)
if area and area != self.area_val:
old_area = area
else:
old_area = None
area = self.area_val
# maybe we are NOW invisible
if visible and not area.visible and old_area:
print 'FIXME area.py:', area.name
if not self.area_name == 'plugin':
if not area.visible or not self.layout:
self.clear()
return
self.tmp_objects = SkinObjects()
self.__draw_background__()
else:
self.tmp_objects = SkinObjects()
# dependencies haven't changed, if no update needed: return
if not self.redraw and not self.update_content_needed():
return
self.update_content()
for b in self.tmp_objects.bgimages:
for t in self.objects.bgimages:
if b == t:
self.objects.bgimages.remove(t)
break
else:
self.screen.add('bg', b)
for b in self.objects.bgimages:
self.screen.remove('bg', b)
for b in self.tmp_objects.rectangles:
for t in self.objects.rectangles:
if b == t:
self.objects.rectangles.remove(t)
break
else:
self.screen.add('alpha', b)
for b in self.objects.rectangles:
self.screen.remove('alpha', b)
for b in self.tmp_objects.images:
for t in self.objects.images:
if b == t:
self.objects.images.remove(t)
break
else:
self.screen.add('content', b)
for b in self.objects.images:
self.screen.remove('content', b)
for b in self.tmp_objects.text:
for t in self.objects.text:
if b == t:
self.objects.text.remove(t)
break
else:
self.screen.add('content', b)
for b in self.objects.text:
self.screen.remove('content', b)
# save and exit
self.objects = self.tmp_objects
def scan_for_text_view(self, menu):
"""
scan if we have to fall back to text view. This will be done if some
items have images and all images are the same. And the number of items
must be greater 5. With that the skin will fall back to text view for
e.g. mp3s inside a folder with cover file
"""
try:
self.use_text_view = menu.skin_force_text_view
try:
self.use_images = menu.skin_default_has_images
self.use_description = menu.skin_default_has_description
except:
self.use_images = False
self.use_description = False
return
except:
pass
image = None
folder = 0
menu.skin_default_has_images = False
menu.skin_default_has_description = False
if hasattr(menu, 'is_submenu'):
menu.skin_default_has_images = True
for i in menu.choices:
if i.image:
menu.skin_default_has_images = True
if i['description'] or i.type:
# have have a description if description is an attribute
# or when the item has a type (special skin handling here)
menu.skin_default_has_description = True
if menu.skin_default_has_images and menu.skin_default_has_description:
break
self.use_images = menu.skin_default_has_images
self.use_description = menu.skin_default_has_description
if len(menu.choices) < 6:
try:
if menu.choices[0].info_type == 'track':
menu.skin_force_text_view = True
self.use_text_view = True
return
except:
pass
for i in menu.choices:
if config.SKIN_FORCE_TEXTVIEW_STYLE == 1 and \
i.type == 'dir' and not i.media:
# directory with few items and folder:
self.use_text_view = False
return
if image and i.image != image:
menu.skin_force_text_view = False
self.use_text_view = False
return
image = i.image
menu.skin_force_text_view = True
self.use_text_view = True
return
for i in menu.choices:
if i.type == 'dir':
folder += 1
# directory with mostly folder:
if config.SKIN_FORCE_TEXTVIEW_STYLE == 1 and folder > 3 and not
i.media:
self.use_text_view = False
return
if image and i.image != image:
menu.skin_force_text_view = False
self.use_text_view = False
return
image = i.image
menu.skin_force_text_view = True
self.use_text_view = True
def calc_geometry(self, object, copy_object=0):
"""
calculate the real values of the object (e.g. content) based
on the geometry of the area
"""
if copy_object:
object = copy.copy(object)
font_h=0
if isinstance(object.width, str):
object.width = int(eval(object.width, {'MAX':self.area_val.width}))
if isinstance(object.height, str):
object.height = int(eval(object.height, {'MAX':self.area_val.height}))
object.x += self.area_val.x
object.y += self.area_val.y
if not object.width:
object.width = self.area_val.width
if not object.height:
object.height = self.area_val.height
if object.width + object.x > self.area_val.width + self.area_val.x:
object.width = self.area_val.width - object.x
if object.height + object.y > self.area_val.height + self.area_val.y:
object.height = self.area_val.height + self.area_val.y - object.y
return object
def get_item_rectangle(self, rectangle, item_w, item_h):
"""
calculates the values for a rectangle to fit item_w and item_h
inside it.
"""
r = copy.copy(rectangle)
# get the x and y value, based on MAX
if isinstance(r.x, str):
r.x = int(eval(r.x, {'MAX':item_w}))
if isinstance(r.y, str):
r.y = int(eval(r.y, {'MAX':item_h}))
# set rect width and height to something
if not r.width:
r.width = item_w
if not r.height:
r.height = item_h
# calc width and height based on MAX settings
if isinstance(r.width, str):
r.width = int(eval(r.width, {'MAX':item_w}))
if isinstance(r.height, str):
r.height = int(eval(r.height, {'MAX':item_h}))
# correct item_w and item_h to fit the rect
item_w = max(item_w, r.width)
item_h = max(item_h, r.height)
if r.x < 0:
item_w -= r.x
if r.y < 0:
item_h -= r.y
# return needed width and height to fit original width and height
# and the rectangle attributes
return max(item_w, r.width), max(item_h, r.height), r
def init_vars(self, settings, display_type, widget_type = 'menu'):
"""
check which layout is used and set variables for the object
"""
redraw = self.redraw
self.settings = settings
if widget_type == 'menu':
# get the correct <menu>
if display_type and settings.special_menu.has_key(display_type):
area = settings.special_menu[display_type]
else:
name = 'default'
if self.use_description:
name += ' description'
if not self.use_images:
name += ' no image'
area = settings.default_menu[name]
# get the correct style based on display_style
if len(area.style) > self.display_style:
area = area.style[self.display_style]
else:
try:
area = area.style[0]
except IndexError:
print 'index error for %s %s' % (self.display_style, widget_type)
raise
if area[0] and (not self.use_text_view):
area = area[0]
elif area[1]:
area = area[1]
else:
print 'want to fall back, but no text view defined'
area = area[0]
else:
area = settings.sets[widget_type]
if hasattr(area, 'style'):
try:
area = area.style[self.display_style][1]
except:
area = area.style[0][1]
if self.area_name == 'plugin':
if not self.area_val:
self.area_val = xml_skin.Area(self.area_name)
self.area_val.visible = True
self.area_val.r = (0, 0, osd.width, osd.height)
return True
else:
try:
area = getattr(area, self.area_name)
except AttributeError:
try:
area = area.areas[self.area_name]
except (KeyError, AttributeError):
print 'no skin information for %s:%s' % (widget_type,
self.area_name)
area = xml_skin.Area(self.area_name)
area.visible = False
if (not self.area_val) or area != self.area_val:
self.area_val = area
redraw = True
if not area.layout:
return redraw
old_layout = self.layout
self.layout = area.layout
if old_layout and old_layout != self.layout:
redraw = True
area.r = (area.x, area.y, area.width, area.height)
return redraw
def __draw_background__(self):
"""
draw the <background> of the area
"""
area = self.area_val
last_watermark = None
try:
if self.watermark:
last_watermark = self.watermark
try:
if self.menu.selected.image != self.watermark:
self.watermark = None
self.redraw = True
except:
pass
except:
pass
for bg in self.layout.background:
bg = copy.copy(bg)
if isinstance(bg, xml_skin.Image) and bg.visible:
self.calc_geometry(bg)
imagefile = ''
# if this is the real background image, ignore the
# OVERSCAN to fill the whole screen
if bg.label == 'background':
bg.x -= config.OSD_OVERSCAN_X
bg.y -= config.OSD_OVERSCAN_Y
bg.width += 2 * config.OSD_OVERSCAN_X
bg.height += 2 * config.OSD_OVERSCAN_Y
if bg.label == 'watermark' and self.menu.selected.image:
imagefile = self.menu.selected.image
if last_watermark != imagefile:
self.redraw = True
self.watermark = imagefile
else:
imagefile = bg.filename
# set to 'background' to be added to that image list
if bg.label != 'top':
bg.label = 'background'
if imagefile:
cname = '%s-%s-%s' % (imagefile, bg.width, bg.height)
image = self.imagecache[cname]
if not image:
cache = vfs.getoverlay('%s.raw-%sx%s' % (imagefile, bg.width,
bg.height))
if os.path.isfile(cache) and \
os.stat(cache)[stat.ST_MTIME] > \
os.stat(imagefile)[stat.ST_MTIME]:
f = open(cache, 'r')
image = pygame.image.fromstring(str().join(f.readlines()),
(bg.width,bg.height),
'RGBA')
f.close()
self.imagecache[cname] = image
if not image:
image = osd.loadbitmap(imagefile)
if image:
image = pygame.transform.scale(image,(bg.width,bg.height))
f = vfs.open(cache, 'w')
f.write(pygame.image.tostring(image, 'RGBA'))
f.close()
self.imagecache[cname] = image
if image:
self.drawimage(image, bg)
elif isinstance(bg, xml_skin.Rectangle):
self.calc_geometry(bg)
self.drawroundbox(bg.x, bg.y, bg.width, bg.height, bg)
# functions for the area to draw stuff on the screen
#
# drawroundbox
# drawimage
# drawstring
def drawroundbox(self, x, y, width, height, rect, redraw=True):
"""
draw a round box ... or better stores the information about this call
in a variable. The real drawing is done inside draw()
"""
try:
self.tmp_objects.rectangles.append(Rectangle(x, y, x + width, y + height,
rect.bgcolor, rect.size,
rect.color, rect.radius ))
except AttributeError:
self.tmp_objects.rectangles.append(Rectangle(x, y, x + width, y + height,
rect[0], rect[1], rect[2],
rect[3]))
def drawstring(self, text, font, content, x=-1, y=-1, width=None, height=None,
align_h = None, align_v = None, mode='hard', ellipses='...',
dim=True):
"""
writes a text ... or better stores the information about this call
in a variable. The real drawing is done inside draw()
"""
if not text:
return (0,0,0,0)
# set default values from 'content'
if x == -1:
x = content.x
if y == -1:
y = content.y
if width == None:
width = content.width
if height == None:
height = content.height
if not align_h and content:
align_h = content.align
if not align_h:
align_h = 'left'
if not align_v and content:
align_v = content.valign
if not align_v:
align_v = 'top'
height2 = height
if height2 == -1:
height2 = font.h + 2
self.tmp_objects.text.append(Text(x, y, x+width, y+height2, text, font, height,
align_h, align_v, mode, ellipses, dim))
def loadimage(self, image, val, redraw=True):
"""
load an image (use self.imagecache)
"""
if image.find(config.ICON_DIR) == 0 and image.find(self.settings.icon_dir) ==
-1:
new_image = os.path.join(self.settings.icon_dir,
image[len(config.ICON_DIR)+1:])
if os.path.isfile(new_image):
image = new_image
if isinstance(val, tuple) or isinstance(val, list):
w = val[0]
h = val[1]
else:
w = val.width
h = val.height
cname = '%s-%s-%s' % (image, w, h)
cimage = self.imagecache[cname]
if not cimage:
cimage = osd.loadbitmap(image)
if not cimage:
return
if w == -1:
w = h * cimage.get_width() / cimage.get_height()
if h == -1:
h = w * cimage.get_height() / cimage.get_width()
if w > 0 and h > 0:
cimage = pygame.transform.scale(cimage, (w, h))
self.imagecache[cname] = cimage
return cimage
def drawimage(self, image, val, background=False):
"""
draws an image ... or better stores the information about this call
in a variable. The real drawing is done inside draw()
"""
if not image:
return 0,0
if isstring(image):
if isinstance(val, tuple):
image = self.loadimage(String(image), val[2:])
else:
image = self.loadimage(String(image), val)
if not image:
return 0,0
if isinstance(val, tuple):
if background:
o = self.tmp_objects.bgimages
else:
o = self.tmp_objects.images
o.append(Image(val[0], val[1], val[0] + image.get_width(),
val[1] + image.get_height(), image))
return image.get_width(), image.get_height()
try:
if background or val.label == 'background':
self.tmp_objects.bgimages.append(Image(val.x, val.y, val.x + val.width,
val.y + val.height, image))
return val.width, val.height
except:
pass
self.tmp_objects.images.append(Image(val.x, val.y, val.x + val.width,
val.y + val.height, image))
return val.width, val.height
# compatibility functions, will be removed
write_text = drawstring
draw_image = drawimage
--- NEW FILE: screen.py ---
# -*- coding: iso-8859-1 -*-
# -----------------------------------------------------------------------
# screen.py - The screen for the Freevo areas to draw on
# -----------------------------------------------------------------------
# $Id: screen.py,v 1.1 2004/07/16 19:51:54 dischi Exp $
#
# -----------------------------------------------------------------------
# $Log: screen.py,v $
# Revision 1.1 2004/07/16 19:51:54 dischi
# new screen design test code, read future_ideas
#
# Revision 1.12 2004/07/10 12:33:41 dischi
# header cleanup
#
# Revision 1.11 2004/04/25 11:23:59 dischi
# Added support for animations. Most of the code is from Viggo Fredriksen
#
# -----------------------------------------------------------------------
# Freevo - A Home Theater PC framework
# Copyright (C) 2002 Krister Lagerstrom, et al.
# Please see the file freevo/Docs/CREDITS for a complete list of authors.
#
# 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 MER-
# CHANTABILITY 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.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# -----------------------------------------------------------------------
import config
import osd
import pygame
import os
osd = osd.get_singleton()
singleton = None
def get_singleton():
global singleton
if not singleton:
if hasattr(config, 'BMOVL_OSD_VIDEO'):
print
print 'Activating skin bmovl output'
print 'THIS IS A TEST, DO NOT USE ANYTHING EXCEPT MENUS'
print
MPLAYER_SOFTWARE_SCALER = "-subfont-text-scale 15 -sws 2 -vf scale=%s:-2,"\
"expand=%s:%s,bmovl=1:0:/tmp/bmovl "\
"-font /usr/share/mplayer/fonts/"\
"font-arial-28-iso-8859-2/font.desc" % \
( osd.width, osd.width, osd.height )
import childapp
childapp.ChildApp2([config.MPLAYER_CMD] +
MPLAYER_SOFTWARE_SCALER.split(' ') +
[config.BMOVL_OSD_VIDEO])
singleton = BmovlScreen('/tmp/bmovl')
else:
singleton = PygameScreen()
return singleton
class SkinObjects:
"""
object which stores the different types of objects
an area wants to draw
"""
def __init__(self):
self.bgimages = []
self.rectangles = []
self.images = []
self.text = []
self.bg = []
self.alpha = []
self.content = []
class Surface:
def __init__(self, osd, alpha):
self.osd = osd
if alpha:
self.screen = self.osd.screen.convert_alpha()
self.screen.fill((0xff,0,0,0))
else:
self.screen = self.osd.screen.convert()
self.fill = self.screen.fill
self.lock = self.screen.lock
self.lock = self.screen.unlock
self.blit = self.screen.blit
def drawroundbox(self, *arg1, **arg2):
arg2['layer'] = self.screen
return self.osd.drawroundbox(*arg1, **arg2)
def drawstringframed(self, *arg1, **arg2):
arg2['layer'] = self.screen
return self.osd.drawstringframed(*arg1, **arg2)
class GUIObject:
def __init__(self, x1, y1, x2, y2):
self.x1 = x1
self.y1 = y1
self.x2 = x2
self.y2 = y2
self.surface = None
def draw(self, surface, rect=None):
pass
def in_update(x1, y1, x2, y2, update_area, full=False):
if full:
for ux1, uy1, ux2, uy2 in update_area:
# if the area is not complete inside the area but is inside on
# some pixels, return False
if (not (ux1 >= x1 and uy1 >= y1 and ux2 <= x2 and uy2 <= y2)) and \
(not (x2 < ux1 or y2 < uy1 or x1 > ux2 or y1 > uy2)):
return False
return True
for ux1, uy1, ux2, uy2 in update_area:
if not (x2 < ux1 or y2 < uy1 or x1 > ux2 or y1 > uy2):
return True
return False
class Image(GUIObject):
def __init__(self, x1, y1, x2, y2, image):
GUIObject.__init__(self, x1, y1, x2, y2)
self.image = image
def draw(self, surface, rect=None):
if not rect:
surface.blit(self.image, (self.x1, self.y1))
elif not in_update(self.x1, self.y1, self.x2, self.y2, rect):
pass
elif len(rect) == 1:
x1, y1, x2, y2 = rect[0]
if not (self.x2 < x1 or self.y2 < y1 or self.x1 > x2 or self.y1 > y2):
surface.blit(self.image, rect[0][:2], (x1-self.x1, y1-self.y1, x2-x1,
y2-y1))
else:
surface.blit(self.image, (self.x1, self.y1))
def __cmp__(self, o):
return self.x1 != o.x1 or self.y1 != o.y1 or self.x2 != o.x2 or \
self.y2 != o.y2 or self.image != o.image
class Rectangle(GUIObject):
def __init__(self, x1, y1, x2, y2, bgcolor, size, color, radius):
GUIObject.__init__(self, x1, y1, x2, y2)
self.bgcolor = bgcolor
self.size = size
self.color = color
self.radius = radius
def draw(self, surface, rect=None):
if rect:
if not in_update(self.x1, self.y1, self.x2, self.y2, rect):
return
if in_update(self.x1+self.size+self.radius, self.y1+self.size+self.radius,
self.x2-self.size-self.radius, self.y2-self.size-self.radius,
rect, full=True):
surface.drawroundbox(self.x1, self.y1, self.x2, self.y2,
color=self.bgcolor)
return
surface.drawroundbox(self.x1, self.y1, self.x2, self.y2, color=self.bgcolor,
border_size=self.size, border_color=self.color,
radius=self.radius)
def __cmp__(self, o):
return self.x1 != o.x1 or self.y1 != o.y1 or self.x2 != o.x2 or \
self.y2 != o.y2 or self.bgcolor != o.bgcolor or \
self.size != o.size or self.color != o.color or self.radius != o.radius
class Text(GUIObject):
def __init__(self, x1, y1, x2, y2, text, font, height, align_h, align_v, mode,
ellipses, dim):
GUIObject.__init__(self, x1, y1, x2, y2)
self.text = text
self.font = font
self.height = height
self.align_h = align_h
self.align_v = align_v
self.mode = mode
self.ellipses = ellipses
self.dim = dim
self.surface = None
def draw(self, surface, rect=None):
if rect and not in_update(self.x1, self.y1, self.x2, self.y2, rect):
return
surface.drawstringframed(self.text, self.x1, self.y1, self.x2 - self.x1,
self.height, self.font, align_h = self.align_h,
align_v = self.align_v, mode=self.mode,
ellipses=self.ellipses, dim=self.dim)
def __cmp__(self, o):
return self.x1 != o.x1 or self.y1 != o.y1 or self.x2 != o.x2 or \
self.y2 != o.y2 or self.text != o.text or self.font != o.font or \
self.height != o.height or self.align_h != o.align_h or \
self.align_v != o.align_v or self.mode != o.mode or \
self.ellipses != o.ellipses or self.dim != o.dim
class PygameScreen:
"""
this call is a set of surfaces for the area to do it's job
"""
def __init__(self):
self.s_content = osd.screen.convert()
self.s_alpha = Surface(osd, True)
self.s_bg = self.s_content.convert()
self.s_screen = Surface(osd, False)
self.update_bg = []
self.update_alpha = []
self.update_content = []
self.drawlist = SkinObjects()
self.s_alpha.fill((0,0,0,0))
def clear(self):
self.update_bg = []
self.update_alpha = []
self.update_content = []
self.drawlist = SkinObjects()
def add(self, layer, object):
"""
layer == bg, alpha or content
"""
drawlist = getattr(self.drawlist, layer)
for o in drawlist:
if o == object:
return False
drawlist.append(object)
getattr(self, 'update_%s' % layer).append((object.x1, object.y1,
object.x2, object.y2))
return True
def remove(self, layer, object):
"""
layer == bgimages, rectangles or content
"""
drawlist = getattr(self.drawlist, layer)
for o in drawlist:
if o == object:
getattr(self, 'update_%s' % layer).append((o.x1, o.y1, o.x2, o.y2))
drawlist.remove(o)
return True
return False
def show(self, force_redraw=False):
"""
the main drawing function
"""
if osd.must_lock:
# only lock s_alpha layer, because only there
# are pixel operations (round rectangle)
self.s_alpha.lock()
if force_redraw:
_debug_('show, force update', 2)
self.update_bg = [(0,0,osd.width, osd.height)]
self.update_alpha = []
self.update_content = []
update_area = self.update_alpha
# if the background has some changes ...
if self.update_bg:
for o in self.drawlist.bg:
o.draw(self.s_bg, self.update_bg)
update_area += self.update_bg
# rectangles
if update_area:
self.s_alpha.fill((0,0,0,0))
for o in self.drawlist.alpha:
o.draw(self.s_alpha, update_area)
# and than blit only the changed parts of the screen
for x0, y0, x1, y1 in update_area:
self.s_content.blit(self.s_bg, (x0, y0), (x0, y0, x1-x0, y1-y0))
self.s_content.blit(self.s_alpha.screen, (x0, y0), (x0, y0, x1-x0,
y1-y0))
update_area += self.update_content
layer = self.s_screen
for x0, y0, x1, y1 in update_area:
layer.blit(self.s_content, (x0, y0), (x0, y0, x1-x0, y1-y0))
# if something changed redraw all content objects
if update_area:
ux1, uy1, ux2, uy2 = osd.width, osd.height, 0, 0
for a in update_area:
ux1 = min(ux1, a[0])
uy1 = min(uy1, a[1])
ux2 = max(ux2, a[2])
uy2 = max(uy2, a[3])
rect = [(ux1, uy1, ux2, uy2)]
for o in self.drawlist.content:
o.draw(layer, rect)
if not update_area:
if osd.must_lock:
self.s_alpha.unlock()
self.update_bg = []
self.update_alpha = []
self.update_content = []
return None
rect = (osd.width, osd.height, 0, 0)
for u in update_area:
osd.screenblit(layer.screen, (u[0], u[1]),
(u[0], u[1], u[2] - u[0], u[3] - u[1]))
rect = ( min(u[0], rect[0]), min(u[1], rect[1]),
max(u[2], rect[2]), max(u[3], rect[3]))
rect = (rect[0], rect[1], rect[2] - rect[0], rect[3] - rect[1])
if osd.must_lock:
self.s_alpha.unlock()
self.update_bg = []
self.update_alpha = []
self.update_content = []
return rect
class BmovlScreen(PygameScreen):
def __init__(self, fifo):
PygameScreen.__init__(self)
self.s_bg = self.s_content.convert_alpha()
self.s_content = osd.screen.convert_alpha()
self.s_screen = Surface(osd, True)
self.fifo = os.open(fifo, os.O_WRONLY)
try:
os.write(self.fifo, 'SHOW\n')
except OSError, e:
print e
pass
def show(self, force_redraw=False):
"""
the main drawing function
"""
if osd.must_lock:
# only lock s_alpha layer, because only there
# are pixel operations (round rectangle)
self.s_alpha.lock()
if force_redraw:
_debug_('show, force update', 2)
self.update_bg = [(0,0,osd.width, osd.height)]
self.update_alpha = []
self.update_content = []
self.s_bg.fill((0,0,0, 0))
self.s_alpha.screen.fill((0,0,0,0))
self.s_content.fill((0,0,0, 0))
self.s_screen.screen.fill((0,0,0, 0))
if not self.update_alpha + self.update_bg + self.update_content:
print 'nothing to update'
return (0,0,0,0)
rect = (osd.width, osd.height, 0, 0)
for u in self.update_alpha + self.update_bg + self.update_content:
rect = ( min(u[0], rect[0]), min(u[1], rect[1]),
max(u[2], rect[2]), max(u[3], rect[3]))
blitrect = (rect[0], rect[1], rect[2] - rect[0], rect[3] - rect[1])
update_area = [ rect ]
# if the background has some changes ...
for o in self.drawlist.bg:
if o.x1 == 0 and o.y1 == 0 and o.x2 == osd.width and o.y2 == osd.height:
continue
o.draw(self.s_bg, update_area)
# rectangles
for o in self.drawlist.alpha:
o.draw(self.s_alpha, update_area)
# and than blit only the changed parts of the screen
for x0, y0, x1, y1 in update_area:
self.s_content.blit(self.s_bg, (x0, y0), (x0, y0, x1-x0, y1-y0))
self.s_content.blit(self.s_alpha.screen, (x0, y0), (x0, y0, x1-x0, y1-y0))
layer = self.s_screen
for x0, y0, x1, y1 in update_area:
layer.blit(self.s_content, (x0, y0), (x0, y0, x1-x0, y1-y0))
# if something changed redraw all content objects
ux1, uy1, ux2, uy2 = osd.width, osd.height, 0, 0
for a in update_area:
ux1 = min(ux1, a[0])
uy1 = min(uy1, a[1])
ux2 = max(ux2, a[2])
uy2 = max(uy2, a[3])
rect = [(ux1, uy1, ux2, uy2)]
for o in self.drawlist.content:
o.draw(layer, rect)
if osd.must_lock:
self.s_alpha.unlock()
self.update_bg = []
self.update_alpha = []
self.update_content = []
print blitrect
surface = layer.screen.subsurface(blitrect)
try:
os.write(self.fifo, 'RGBA32 %d %d %d %d %d %d\n' % \
(surface.get_width(), surface.get_height(),
blitrect[0], blitrect[1], 0, 0))
os.write(self.fifo, pygame.image.tostring(surface, 'RGBA'))
except OSError, e:
print e
return blitrect
--- NEW FILE: main.py ---
# -*- coding: iso-8859-1 -*-
# -----------------------------------------------------------------------
# skin_main1.py - Freevo default skin
# -----------------------------------------------------------------------
# $Id: main.py,v 1.1 2004/07/16 19:51:54 dischi Exp $
#
# Notes:
# Todo:
#
# -----------------------------------------------------------------------
# $Log: main.py,v $
# Revision 1.1 2004/07/16 19:51:54 dischi
# new screen design test code, read future_ideas
#
# Revision 1.47 2004/07/10 12:33:41 dischi
# header cleanup
#
# Revision 1.46 2004/07/09 11:20:12 dischi
# do not load outdated skins
#
# Revision 1.45 2004/03/14 11:42:34 dischi
# make idlebar have a background image
#
# -----------------------------------------------------------------------
# Freevo - A Home Theater PC framework
# Copyright (C) 2002 Krister Lagerstrom, et al.
# Please see the file freevo/Docs/CREDITS for a complete list of authors.
#
# 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 MER-
# CHANTABILITY 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.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# -----------------------------------------------------------------------
import os, copy
import stat
import traceback
import config
import util
import osd
from area import Skin_Area
from gui import GUIObject
import xml_skin
import screen
# Create the OSD object
osd = osd.get_singleton()
###############################################################################
# Skin main functions
###############################################################################
class Skin:
"""
main skin class
"""
Rectange = xml_skin.Rectangle
Image = xml_skin.Image
Area = Skin_Area
def __init__(self):
"""
init the skin engine
"""
global skin_engine
skin_engine = self
self.display_style = { 'menu' : 0 }
self.force_redraw = True
self.last_draw = None, None, None
self.screen = screen.get_singleton()
self.areas = {}
self.all_areas = []
# load default areas
from listing_area import Listing_Area
from tvlisting_area import TVListing_Area
from view_area import View_Area
from info_area import Info_Area
from default_areas import Screen_Area, Title_Area, Subtitle_Area, Plugin_Area
for a in ( 'screen', 'title', 'subtitle', 'view', 'listing', 'info', 'plugin'):
self.areas[a] = eval('%s_Area()' % a.capitalize())
self.areas['tvlisting'] = TVListing_Area()
self.storage_file = os.path.join(config.FREEVO_CACHEDIR, 'skin-%s' %
os.getuid())
self.storage = util.read_pickle(self.storage_file)
if self.storage:
if not config.SKIN_XML_FILE:
config.SKIN_XML_FILE = self.storage['SKIN_XML_FILE']
else:
_debug_('skin forced to %s' % config.SKIN_XML_FILE, 2)
else:
if not config.SKIN_XML_FILE:
config.SKIN_XML_FILE = config.SKIN_DEFAULT_XML_FILE
self.storage = {}
# load the fxd file
self.settings = xml_skin.XMLSkin()
self.set_base_fxd(config.SKIN_XML_FILE)
def cachename(self, filename):
"""
create cache name
"""
geo = '%sx%s-%s-%s' % (osd.width, osd.height, config.OSD_OVERSCAN_X,
config.OSD_OVERSCAN_Y)
return vfs.getoverlay('%s.skin-%s-%s' % (filename, config.SKIN_XML_FILE, geo))
def save_cache(self, settings, filename):
"""
cache the fxd skin settings in 'settings' to the OVERLAY_DIR cachfile
for filename and this resolution
"""
cache = self.cachename(filename)
if cache:
# delete font object, because it can't be pickled
for f in settings.font:
del settings.font[f].font
# save object and version information
util.save_pickle((xml_skin.FXD_FORMAT_VERSION, settings), cache)
# restore font object
for f in settings.font:
settings.font[f].font = osd.getfont(settings.font[f].name,
settings.font[f].size)
def load_cache(self, filename):
"""
load a skin cache file
"""
if hasattr(self, '__last_load_cache__') and self.__last_load_cache__[0] ==
filename:
return self.__last_load_cache__[1]
if not os.path.isfile(filename):
return None
cache = self.cachename(filename)
if not cache:
return None
if not os.path.isfile(cache):
return None
version, settings = util.read_pickle(cache)
if not settings or version != xml_skin.FXD_FORMAT_VERSION:
return None
pdir = os.path.join(config.SHARE_DIR, 'skins/plugins')
if os.path.isdir(pdir):
ffiles = util.match_files(pdir, [ 'fxd' ])
else:
ffiles = []
for f in settings.fxd_files:
if not os.path.dirname(f).endswith(pdir):
ffiles.append(f)
# check if all files used by the skin are not newer than
# the cache file
ftime = os.stat(cache)[stat.ST_MTIME]
for f in ffiles:
if os.stat(f)[stat.ST_MTIME] > ftime:
return None
# restore the font objects
for f in settings.font:
settings.font[f].font = osd.getfont(settings.font[f].name,
settings.font[f].size)
self.__last_load_cache__ = filename, settings
return settings
def register(self, type, areas):
"""
register a new type objects to the skin
"""
setattr(self, '%s_areas' % type, [])
for a in areas:
if isinstance(a, str):
getattr(self, '%s_areas' % type).append(self.areas[a])
else:
getattr(self, '%s_areas' % type).append(a)
def delete(self, type):
"""
delete informations about a special skin type
"""
exec('del self.%s_areas' % type)
self.last_draw = None, None, None
def change_area(self, name, module, object):
"""
replace an area with the code from module.object() from skins/plugins
"""
exec('import skins.plugins.%s' % module)
self.areas[name] = eval('skins.plugins.%s.%s()' % (module, object))
def set_base_fxd(self, name):
"""
set the basic skin fxd file
"""
config.SKIN_XML_FILE = os.path.splitext(os.path.basename(name))[0]
_debug_('load basic skin settings: %s' % config.SKIN_XML_FILE)
# try to find the skin xml file
if not self.settings.load(name, clear=True):
print "skin not found, using fallback skin"
self.settings.load('basic.fxd', clear=True)
for dir in config.cfgfilepath:
local_skin = '%s/local_skin.fxd' % dir
if os.path.isfile(local_skin):
_debug_('Skin: Add local config %s to skin' % local_skin,2)
self.settings.load(local_skin)
break
self.storage['SKIN_XML_FILE'] = config.SKIN_XML_FILE
util.save_pickle(self.storage, self.storage_file)
if self.storage.has_key(config.SKIN_XML_FILE):
self.display_style['menu'] = self.storage[config.SKIN_XML_FILE]
else:
self.display_style['menu'] = 0
def load(self, filename, copy_content = 1):
"""
return an object with new skin settings
"""
_debug_('load additional skin info: %s' % filename)
if filename and vfs.isfile(vfs.join(filename, 'folder.fxd')):
filename = vfs.abspath(os.path.join(filename, 'folder.fxd'))
elif filename and vfs.isfile(filename):
filename = vfs.abspath(filename)
else:
return None
settings = self.load_cache(filename)
if settings:
return settings
if copy_content:
settings = copy.copy(self.settings)
else:
settings = xml_skin.XMLSkin()
if not settings.load(filename, clear=True):
return None
self.save_cache(settings, filename)
return settings
def get_skins(self):
"""
return a list of all possible skins with name, image and filename
"""
ret = []
skin_files = util.match_files(os.path.join(config.SKIN_DIR, 'main'), ['fxd'])
# image is not usable stand alone
skin_files.remove(os.path.join(config.SKIN_DIR, 'main/image.fxd'))
skin_files.remove(os.path.join(config.SKIN_DIR, 'main/basic.fxd'))
for skin in skin_files:
name = os.path.splitext(os.path.basename(skin))[0]
if os.path.isfile('%s.png' % os.path.splitext(skin)[0]):
image = '%s.png' % os.path.splitext(skin)[0]
elif os.path.isfile('%s.jpg' % os.path.splitext(skin)[0]):
image = '%s.jpg' % os.path.splitext(skin)[0]
else:
image = None
ret += [ ( name, image, skin ) ]
return ret
def get_settings(self):
"""
return the current loaded settings
"""
return self.settings
def toggle_display_style(self, menu):
"""
Toggle display style
"""
if isinstance(menu, str):
if not self.display_style.has_key(menu):
self.display_style[menu] = 0
self.display_style[menu] = (self.display_style[menu] + 1) % \
len(self.settings.sets[menu].style)
return 1
if menu.force_skin_layout != -1:
return 0
if menu and menu.skin_settings:
settings = menu.skin_settings
else:
settings = self.settings
if settings.special_menu.has_key(menu.item_types):
area = settings.special_menu[menu.item_types]
else:
area = settings.default_menu['default']
if self.display_style['menu'] >= len(area.style):
self.display_style['menu'] = 0
self.display_style['menu'] = (self.display_style['menu'] + 1) % len(area.style)
self.storage[config.SKIN_XML_FILE] = self.display_style['menu']
util.save_pickle(self.storage, self.storage_file)
return 1
def get_display_style(self, menu=None):
"""
return current display style
"""
if isinstance(menu, str):
if not self.display_style.has_key(menu):
self.display_style[menu] = 0
return self.display_style[menu]
if menu:
if menu.force_skin_layout != -1:
return menu.force_skin_layout
return self.display_style['menu']
def __find_current_menu__(self, widget):
if not widget:
return None
if not hasattr(widget, 'menustack'):
return self.__find_current_menu__(widget.parent)
return widget.menustack[-1]
def get_popupbox_style(self, widget=None):
"""
This function returns style information for drawing a popup box.
return backround, spacing, color, font, button_default, button_selected
background is ('image', Image) or ('rectangle', Rectangle)
Image attributes: filename
Rectangle attributes: color (of the border), size (of the border),
bgcolor (fill color), radius (round box for the border). There are also
x, y, width and height as attributes, but they may not be needed for the
popup box
button_default, button_selected are XML_item
attributes: font, rectangle (Rectangle)
All fonts are Font objects
attributes: name, size, color, shadow
shadow attributes: visible, color, x, y
"""
menu = self.__find_current_menu__(widget)
if menu and hasattr(menu, 'skin_settings') and menu.skin_settings:
settings = menu.skin_settings
else:
settings = self.settings
layout = settings.popup
background = []
for bg in layout.background:
if isinstance(bg, xml_skin.Image):
background.append(( 'image', bg))
elif isinstance(bg, xml_skin.Rectangle):
background.append(( 'rectangle', bg))
return layout.content, background
def get_font(self, name):
"""
Get the skin font object 'name'. Return the default object if
a font with this name doesn't exists.
"""
try:
return self.settings.font[name]
except:
return self.settings.font['default']
def get_image(self, name):
"""
Get the skin image object 'name'. Return None if
an image with this name doesn't exists.
"""
try:
return self.settings.images[name]
except:
return None
def get_icon(self, name):
"""
Get the icon object 'name'. Return the icon in the theme dir if it
exists, else try the normal image dir. If not found, return ''
"""
icon = util.getimage(os.path.join(self.settings.icon_dir, name))
if icon:
return icon
return util.getimage(os.path.join(config.ICON_DIR, name), '')
def items_per_page(self, (type, object)):
"""
returns the number of items per menu page
(cols, rows) for normal menu and
rows for the tv menu
"""
if type == 'tv':
info = self.areas['tvlisting']
info = info.get_items_geometry(self.settings, object,
self.get_display_style('tv'))
return (info[4], info[-1])
if object.skin_settings:
settings = object.skin_settings
else:
settings = self.settings
menu = None
if type == 'menu':
menu = object
info = self.areas['listing']
rows, cols = info.get_items_geometry(settings, object,
self.get_display_style( menu ))[:2]
return (cols, rows)
def clear(self, osd_update=True):
"""
clean the screen
"""
_debug_('clear: %s' % osd_update, 2)
self.force_redraw = True
osd.clearscreen(osd.COL_BLACK)
if osd_update:
osd.update()
def redraw(self):
"""
redraw the current screen
"""
_debug_('redraw', 2)
if self.last_draw[0] and self.last_draw[1]:
self.draw(self.last_draw[0], self.last_draw[1], self.last_draw[2])
def prepare(self):
"""
prepare the skin
"""
self.settings.prepare()
def draw(self, type, object, menu=None):
"""
draw the object.
object may be a menu widget, a table for the tv menu are an audio item for
the audio player
"""
if isinstance(object, GUIObject):
# handling for gui objects: are they visible? what about children?
if not object.visible:
return
draw_allowed = True
for child in object.children:
draw_allowed = draw_allowed and not child.visible
if not draw_allowed:
self.force_redraw = True
return
settings = self.settings
if type == 'menu':
menu = object.menustack[-1]
if menu.skin_settings:
settings = menu.skin_settings
# XXX FIXME
if len(object.menustack) == 1:
menu.item_types = 'main'
style = self.get_display_style(menu)
else:
try:
if not object.visible:
return
except AttributeError:
pass
style = self.get_display_style(type)
if self.last_draw[0] != type:
self.force_redraw = True
areas = getattr(self, '%s_areas' % type)
for a in self.all_areas:
if not a in areas:
print 'remove area %s' % a
a.clear()
self.all_areas = areas
self.last_draw = type, object, menu
try:
if self.force_redraw:
self.screen.clear()
for a in self.all_areas:
a.clear()
for a in self.all_areas:
a.draw(settings, object, menu, style, type, self.force_redraw)
osd.update([self.screen.show(self.force_redraw)])
self.force_redraw = False
except UnicodeError, e:
print '******************************************************************'
print 'Unicode Error: %s' % e
print 'Please report the following lines to the freevo mailing list'
print 'or with the subject \'[Freevo-Bugreport\] Unicode\' to'
print '[EMAIL PROTECTED]'
print
print traceback.print_exc()
print
print type, object
if type == 'menu':
for i in object.menustack[-1].choices:
print i
print
raise UnicodeError, e
-------------------------------------------------------
This SF.Net email is sponsored by BEA Weblogic Workshop
FREE Java Enterprise J2EE developer tools!
Get your free copy of BEA WebLogic Workshop 8.1 today.
http://ads.osdn.com/?ad_id=4721&alloc_id=10040&op=click
_______________________________________________
Freevo-cvslog mailing list
[EMAIL PROTECTED]
https://lists.sourceforge.net/lists/listinfo/freevo-cvslog