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

Reply via email to