Author: dmeyer
Date: Tue Oct  2 15:57:44 2007
New Revision: 9922

Log:
add grid menu from Joost

Added:
   trunk/ui/src/audio/plugins/album.py
   trunk/ui/src/gui/areas/grid_area.py
   trunk/ui/src/menu/gridmenu.py
   trunk/ui/src/tv/plugins/testguide.py
Modified:
   trunk/ui/AUTHORS
   trunk/ui/share/skins/main/basic.fxd
   trunk/ui/share/skins/main/blurr.fxd
   trunk/ui/src/audio/plugins/config.cxml
   trunk/ui/src/gui/compat.py
   trunk/ui/src/gui/theme.py
   trunk/ui/src/menu/__init__.py
   trunk/ui/src/menu/listing.py
   trunk/ui/src/menu/menu.py
   trunk/ui/src/menu/stack.py
   trunk/ui/src/tv/plugins/config.cxml

Modified: trunk/ui/AUTHORS
==============================================================================
--- trunk/ui/AUTHORS    (original)
+++ trunk/ui/AUTHORS    Tue Oct  2 15:57:44 2007
@@ -24,6 +24,9 @@
 Jose <[EMAIL PROTECTED]>
 o tv enhancements
 
+Joost <[EMAIL PROTECTED]>
+o grid menu and artist/album plugin
+
 
 Contributors Freevo 1.x
 -----------------------

Modified: trunk/ui/share/skins/main/basic.fxd
==============================================================================
--- trunk/ui/share/skins/main/basic.fxd (original)
+++ trunk/ui/share/skins/main/basic.fxd Tue Oct  2 15:57:44 2007
@@ -41,7 +41,7 @@
            <style text="normal text style"/>
        </menu>
 
-       <!-- defualt menu when no item has an image -->
+       <!-- default menu when no item has an image -->
        <menu type="default no image">
            <style text="default no image"/>
        </menu>
@@ -206,6 +206,33 @@
            </content>
        </layout>
 
+       <!-- default grid area -->
+       <layout label="grid">
+           <content type="text" spacing="0">
+
+               <item type="row" font="item" width="80">
+                   <rectangle bgcolor="0x88000066" size="1" color="0x000000" 
x="-5" y="-5"
+                       width="max+10" height="max+10"/>
+               </item>
+
+               <item type="column" font="item" width="175">
+                   <rectangle bgcolor="0x88000066" size="1" color="0x000000" 
x="-5" y="-5"
+                       width="max+10" height="max+10"/>
+               </item>
+
+               <item type="default" font="item">
+                   <rectangle bgcolor="0xff000000" size="1" color="0x000000" 
x="-5"
+                       y="-5" width="max+10" height="max+10"/>
+               </item>
+               
+               <item type="selected" font="selected">
+                   <rectangle bgcolor="selection" size="1" color="0x000000" 
x="-5"
+                       y="-5" width="max+10" height="max+10"/>
+               </item>
+               
+           </content>
+       </layout>
+
        <!-- font used in this layouts -->
        <font label="title area" name="VeraBd.ttf" size="24" color="0xffffff"/>
        <font label="subtitle" name="VeraBd.ttf" size="18" color="0xffffff"/>

Modified: trunk/ui/share/skins/main/blurr.fxd
==============================================================================
--- trunk/ui/share/skins/main/blurr.fxd (original)
+++ trunk/ui/share/skins/main/blurr.fxd Tue Oct  2 15:57:44 2007
@@ -268,6 +268,45 @@
             <info layout="audio info" x="28" y="370" width="273" height="190"/>
         </menuset>
         
+        <menu type="audio grid">
+           <style text="audio grid menu"/>
+        </menu>
+
+        <menuset label="audio grid menu">
+            <screen layout="screen" x="0" y="0" width="800" height="600"/>
+            <title visible="no"/>
+            <grid layout="audio grid" x="10" y="90" width="780" height="500">
+                <image x="765" y="90" width="32" height="32" label="uparrow"
+                   filename="up.png"/>
+                <image x="765" y="max-32" width="32" height="32" 
label="downarrow" 
+                   filename="down.png"/>
+                <image width="16" height="16" label="leftarrow" 
filename="left.png"/>
+                <image width="16" height="16" label="rightarrow" 
filename="right.png"/>
+            </grid>
+        </menuset>
+
+               <!-- audio grid area -->
+               <layout label="audio grid">
+                   <content type="text" spacing="0">
+                       <item type="row" font="item" width="240">
+                           <rectangle bgcolor="0x88000066" size="1" 
color="0x000000" x="-5" y="-5"
+                               width="max+10" height="max+10"/>
+                       </item>
+                       <item type="column" font="item" width="270">
+                           <rectangle bgcolor="0x88000066" size="1" 
color="0x000000" x="-5" y="-5"
+                               width="max+10" height="max+10"/>
+                       </item>
+                       <item type="default" font="item">
+                           <rectangle bgcolor="0xff000000" size="1" 
color="0x000000" x="-5"
+                               y="-5" width="max+10" height="max+10"/>
+                       </item>
+                       <item type="selected" font="selected">
+                           <rectangle bgcolor="selection" size="1" 
color="0x000000" x="-5"
+                               y="-5" width="max+10" height="max+10"/>
+                       </item>
+                   </content>
+               </layout>
+
         <layout label="audio screen">
             <background>
                 <image image="background" x="0" y="0" label="background"/>
@@ -548,6 +587,26 @@
             </background>
         </layout>
         
+        <!-- GUIDE2  -->
+
+        <menu type="tv grid">
+           <style text="tv grid menu"/>
+        </menu>
+
+        <menuset label="tv grid menu">
+            <screen layout="screen" x="0" y="0" width="800" height="600"/>
+            <title visible="no"/>
+            <info layout="tv info" x="10" y="90" width="780" height="140"/>
+            <grid layout="grid" x="10" y="240" width="780" height="350">
+                <image x="765" y="240" width="32" height="32" label="uparrow"
+                   filename="up.png"/>
+                <image x="765" y="max-32" width="32" height="32" 
label="downarrow" 
+                   filename="down.png"/>
+                <image width="16" height="16" label="leftarrow" 
filename="left.png"/>
+                <image width="16" height="16" label="rightarrow" 
filename="right.png"/>
+            </grid>
+        </menuset>
+
         <!-- GUIDE  -->
 
         <menuset label="tv menu">

Added: trunk/ui/src/audio/plugins/album.py
==============================================================================
--- (empty file)
+++ trunk/ui/src/audio/plugins/album.py Tue Oct  2 15:57:44 2007
@@ -0,0 +1,149 @@
+# -*- coding: iso-8859-1 -*-
+# -----------------------------------------------------------------------------
+# album.py - browse collection based on album/artist tag
+# -----------------------------------------------------------------------------
+# $Id: album.py 9541 2007-05-01 18:46:35Z dmeyer $
+#
+# This plugin adds an item 'Browse by Artists albums' to the audio menu.
+# It views the albums per artist in a grid view.
+#
+# This plugin is also a simple example how to write plugins and how to use
+# kaa.beacon in freevo.
+#
+# -----------------------------------------------------------------------------
+# Freevo - A Home Theater PC framework
+# Copyright (C) 2007 Dirk Meyer, et al.
+#
+# First Edition: Joost <[EMAIL PROTECTED]>
+# Maintainer:    Dirk Meyer <[EMAIL PROTECTED]>
+#
+# Please see the file AUTHORS 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
+#
+# -----------------------------------------------------------------------------
+
+# Kaa imports
+import kaa.beacon
+from kaa.strutils import str_to_unicode
+
+# Freevo imports
+from freevo.ui.mainmenu import MainMenuPlugin
+from freevo.ui.menu import Item, ItemList, ActionItem, Menu, Action, GridMenu, 
MediaItem
+from freevo.ui.playlist import Playlist
+from freevo.ui.audio import AudioItem
+from freevo.ui.directory import DirItem
+
+class AlbumItem(MediaItem):
+    """
+    Item for on Album (or all) for an artist.
+    """
+    def __init__(self, artist, album, parent):
+        MediaItem.__init__(self, parent)
+        self.artist = artist
+        self.album = album
+        self.name = _('[ All Songs ]')
+        if album:
+            self.name = album
+
+
+    def browse(self):
+        """
+        Show all items from that artist.
+        """
+        title = str_to_unicode(self.artist)
+        if self.album:
+            query = kaa.beacon.query(artist=self.artist, album=self.album, 
type='audio')
+            title = '%s - %s' % (title, str_to_unicode(self.album))
+        else:
+            query = kaa.beacon.query(artist=self.artist, type='audio')
+        # FIXME: monitor query for live update
+        self.playlist = Playlist(title, query, self, type='audio')
+        self.playlist.browse()
+
+
+    def actions(self):
+        """
+        Actions for this item.
+        """
+        return [ Action(_('Browse Songs'), self.browse) ]
+
+
+
+class ArtistAlbumView(GridMenu):
+    """
+    Item for an artist.
+    """
+    def __init__(self, parent):
+        GridMenu.__init__(self, _('Artist/Albums View'), type = 'audio grid')
+        self.artists_base = 0
+        self.artists = []
+
+        #Query all artists.
+        for artist in kaa.beacon.query(attr='artist', type='audio'):
+            self.artists.append(artist)
+
+        self.col_row_swap = False
+        self.update()
+
+    def update(self):
+        """
+        update the guide area
+        """
+        items = []
+        for artist in self.artists:
+            # FIXME: monitor query for live update
+            query = kaa.beacon.query(attr='album', artist=artist, type='audio')
+    
+            albums = [ AlbumItem(artist, None, self) ]
+            for album in query:
+                albums.append(AlbumItem( artist, album, self))
+                
+            items.append(albums)
+
+        self.set_items(items)
+
+
+    def get_column_name(self, col):
+        """
+        Return the column name
+        """
+        text = 'Album %s' % (self.base_col+col+1)
+        return text
+
+    def get_row_name(self, row):
+        """
+        Return the row name
+        """
+        artist_name = ''
+        artist = self.artists[self.base_row+row]
+        # Work around a beacon bug
+        for part in artist.split(' '):
+            artist_name += ' ' + str_to_unicode(part.capitalize())
+        artist_name = artist_name.strip()
+        return artist_name
+
+
+class PluginInterface(MainMenuPlugin):
+    """
+    Add 'Browse by Artist albums' to the audio menu.
+    """
+
+    def items(self, parent):
+        return [ ActionItem(_('Browse by Artists/Albums'), parent, self.show) ]
+
+    def show(self, parent):
+        artistalbumview = ArtistAlbumView(parent)
+        parent.get_menustack().pushmenu(artistalbumview)

Modified: trunk/ui/src/audio/plugins/config.cxml
==============================================================================
--- trunk/ui/src/audio/plugins/config.cxml      (original)
+++ trunk/ui/src/audio/plugins/config.cxml      Tue Oct  2 15:57:44 2007
@@ -4,4 +4,7 @@
     <group name="artist" plugin="10">
         <desc>Show audio files sorted by artist and album</desc>
     </group>
+    <group name="album" plugin="15">
+        <desc>Show albums sorted by artist in a grid view</desc>
+    </group>
 </config>

Added: trunk/ui/src/gui/areas/grid_area.py
==============================================================================
--- (empty file)
+++ trunk/ui/src/gui/areas/grid_area.py Tue Oct  2 15:57:44 2007
@@ -0,0 +1,364 @@
+# -*- coding: iso-8859-1 -*-
+# -----------------------------------------------------------------------------
+# grid_area.py - A grid area for the Freevo skin
+# -----------------------------------------------------------------------------
+# $Id: grid_area.py 9536 2007-05-01 11:35:34Z dmeyer $
+#
+# This module include the GridArea used in the area code for drawing the
+# grid areas for menus. It inherits from Area (area.py) and the update
+# function will be called to update this area.
+#
+# -----------------------------------------------------------------------------
+# Freevo - A Home Theater PC framework
+# Copyright (C) 2007 Dirk Meyer, et al.
+#
+# First Edition: Joost <[EMAIL PROTECTED]>
+# Maintainer:    Dirk Meyer <[EMAIL PROTECTED]>
+#
+# Please see the file AUTHORS 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
+#
+# -----------------------------------------------------------------------------
+
+__all__ = [ 'GridArea' ]
+
+# python imports
+import copy
+import os
+import math
+import time
+import array
+
+# kaa imports
+from kaa.notifier import Timer
+
+# gui import
+from area import Area
+from freevo.ui.gui.widgets import Image, Rectangle
+
+import logging
+log = logging.getLogger('gui')
+
+from freevo.ui import config
+from freevo.ui.gui import imagelib
+
+class _Geometry(object):
+    """
+    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
+        self.active_col = 0
+        self.active_row = 0
+
+    def __str__(self):
+        return '_Geometry: %s,%s %sx%s' % \
+               (self.x, self.y, self.width, self.height)
+
+class GridArea(Area):
+    """
+    This class defines the GridArea to draw menu grids for the area
+    part of the gui code.
+    """
+    def __init__(self):
+        """
+        Create the Area and define some needed variables
+        """
+        Area.__init__(self, 'grid')
+        self.col_width = 0
+        self.col_height = 0
+        self.cols = 1
+        self.row_width = 0
+        self.row_height = 0
+        self.rows = 1
+        
+        self.row_val     = None
+        self.column_val      = None
+        self.selected_val  = None
+        self.default_val   = None
+        
+        # objects on the area
+        self.row_obj   = []
+        self.col_obj   = []
+        self.up_arrow   = None
+        self.down_arrow = None
+        self.objects    = []
+        self.background = None
+
+
+    def __calc_items_geometry(self):
+        # get the settings
+        settings = self.settings
+
+        # get all values for the different types
+        self.row_val       = settings.types['row']
+        self.column_val    = settings.types['column']
+        self.selected_val  = settings.types['selected']
+        self.default_val   = settings.types['default']
+        
+        # get max font height
+        max_font_h = max(self.selected_val.font.height, 
self.default_val.font.height,
+                     self.row_val.font.height)
+
+
+        # get Row width
+        self.row_width = self.row_val.width
+
+        # get col height
+        self.col_height = self.column_val.font.height
+        if self.column_val.rectangle:
+            r = self.column_val.rectangle.calculate(20, 
self.column_val.font.height)[2]
+            self.col_height = max(self.col_height, r.height + settings.spacing)
+            settings_y = settings.y + r.height + settings.spacing
+        else:
+            settings_y = settings.y + self.column_val.font.height + 
settings.spacing
+
+        # get Col width
+        self.col_width = self.column_val.width
+        self.cols = (settings.width-self.row_width) / self.col_width
+
+        # get item height
+        self.row_height = max_font_h
+        for val in (self.row_val, self.default_val, self.selected_val):
+            if val.rectangle:
+                r = val.rectangle.calculate(20, val.font.height)[2]
+                self.row_height = max(self.row_height, r.height + 
settings.spacing)
+        self.rows = (self.settings.height / self.row_height)-1
+
+
+
+    def __fit_in_rect(self, rectangle, width, height, font_h):
+        """
+        calculates the rectangle geometry and fits it into the area
+        """
+        x = 0
+        y = 0
+
+        r = rectangle.calculate(width, font_h)[2]
+        if r.width > width:
+            r.width, width = width, width - (r.width - width)
+        if r.height > height:
+            r.height, height = height, height - (r.height - height)
+        if r.x < 0:
+            r.x, x = 0, -r.x
+            width -= x
+        if r.y < 0:
+            r.y, y = 0, -r.y
+            height -= y
+        return _Geometry(x, y, width, height), r
+
+
+    def clear(self):
+        for o in self.objects + self.row_obj + self.col_obj:
+            if o:
+                o.unparent()
+        if self.background:
+            self.background.unparent()
+        self.background = None
+        self.objects  = []
+        self.row_obj = []
+        self.col_obj = []
+
+
+    def __draw_col_titles(self, settings, x, y, cols, height):
+        """
+        Draws the column titles
+        """
+        if self.col_obj:
+            return
+        for o in self.col_obj:
+            if o:
+                o.unparent()
+ 
+        col_size = self.col_width
+        if self.column_val.rectangle:
+            rect = self.column_val.rectangle.calculate(col_size, height)[2]
+
+        for i in range( cols ):
+            str = self.menu.get_column_name(i)
+            self.__draw_item(str, x, y, col_size, height, self.column_val, 
self.col_obj)
+
+            x += col_size
+
+
+    def __draw_row_titles(self, settings, x, y, rows, width, height):
+        """
+        Draws the row titles
+        """
+        for o in self.row_obj:
+            if o:
+                o.unparent()
+        self.row_obj = []
+
+        for i in range( rows ):
+            str = self.menu.get_row_name(i)
+            self.__draw_item(str, x, y, width, height, self.row_val, 
self.row_obj)
+
+            #fix this in the skin
+            #self.row_obj.append(self.drawbox(tx0, ty0, width, height, r))
+            
+            y += height
+
+    def __draw_item(self, txt, x, y, width, height, type, gui_obj):
+        """
+        Draws an item, depending on the item type, with or without rect
+        """
+        ig = _Geometry(0, 0, width, height)
+        if type.rectangle:
+            ig, r = self.__fit_in_rect(type.rectangle, width, height, 
type.font.height)
+
+            gui_obj.append(self.drawbox((x+r.x),
+                                        (y+r.y),
+                                        r.width+r.size,
+                                        height+r.size,
+                                        r))
+
+        if txt:
+            string = self.drawstring(txt, type.font,
+                                     self.settings, x+ig.x, y+ig.y,
+                                     ig.width, ig.height,
+                                     align_h=type.align,
+                                     align_v ='center')
+            if string:
+                gui_obj.append(string)
+
+
+    def update(self):
+        """
+        Update the grid area. This function will be called from Area to
+        do the real update.
+        """
+        menu     = self.menu
+        settings = self.settings
+
+        if menu.update_view:
+            menu.update_view = False
+            # layout change, clean everything
+            self.clear()
+            self.last_base_col = menu.base_col
+            self.last_base_row = menu.base_row
+            #Calculate new settings
+            self.__calc_items_geometry()
+        else:
+            #Todo: only redraw new and previous selected items
+            # same layout, only clean 'objects'
+            for o in self.objects:
+                if o: o.unparent()
+            self.objects = []
+
+        menu.cols = self.cols
+        menu.rows = self.rows
+
+        col_x = settings.x + settings.spacing + self.row_width
+
+        col_y = settings.y + settings.spacing
+
+        #draw the background
+        r = _Geometry(0, 0, settings.width, settings.height)
+        if self.row_val.rectangle:
+            r = self.row_val.rectangle.calculate( settings.width, 
settings.height )[ 2 ]
+        if not self.background:
+            self.background = self.drawbox( (settings.x + settings.spacing),
+                                            (settings.y + settings.spacing),
+                                            (((self.cols)*self.col_width + 
self.row_width))+r.size,
+                                            (((self.rows)*self.row_height + 
self.col_height))+r.size,
+                                             r )
+        # Draw the columns(head)
+        self.__draw_col_titles(settings, col_x, col_y, self.cols, 
self.col_height)
+
+        row_x = settings.x + settings.spacing
+        row_y = settings.y + settings.spacing + self.col_height
+        y0 = row_y
+        x0 = col_x
+        # draw the rows(label)
+        self.__draw_row_titles(settings, row_x, row_y, self.rows, 
self.row_width,
+                                     self.row_height)
+        
+        #Prepare the left and right arrows
+        left_arrow_file = None
+        if settings.images['leftarrow']:
+            left_arrow = settings.images['leftarrow']
+            left_arrow_file = left_arrow.filename
+        right_arrow_file = None
+        if settings.images['rightarrow']:
+            right_arrow = settings.images['rightarrow']
+            right_arrow_file = right_arrow.filename
+
+        col_end = col_x+(self.col_width * self.cols)
+
+        #Start drawing the items. Draw row after row.
+        for draw_row in range(self.rows):
+            try:
+                x0 = col_x
+                draw_col = 0
+                while (x0 < col_end):
+                    item = self.menu.get_item(draw_row, draw_col)
+                    if item != None:
+                        if menu.advanced_mode:
+                            size = item[0]
+                            data = item[1]
+                            width = (self.col_width * size) / 100
+                        else:
+                            data = item
+                            width = self.col_width
+                        val = self.default_val
+                        #is the item selected?
+                        if data == menu.selected:
+                            val = self.selected_val
+                        str = data.name
+                        if x0 == col_x:
+                            # draw left arrow
+                            if ( ( (menu.base_col + draw_col) != 0) or 
menu.advanced_mode ) and \
+                               left_arrow_file:
+                                y_arrow = y0 + 
((self.row_height-left_arrow.height)/2)
+                                x_arrow = col_x
+                                left_arrow_settings = (x_arrow, y_arrow, 
left_arrow.width, left_arrow.height)
+                                image = self.drawimage(left_arrow_file, 
left_arrow_settings )
+                                if image:
+                                    self.objects.append(image)
+                            
+                        #check that we don't write outside the columns
+                        if x0+width > col_end:
+                            width = col_end-x0
+                        #draw the item
+                        self.__draw_item(str, x0, y0, width, self.row_height,
+                                         val, self.objects)
+                        x0 += width
+                    else:
+                        #todo check this...
+                        x0 += self.col_width
+                    draw_col += 1
+
+                # draw right arrow, Draw right arrow if there are more items 
to come.
+                item = self.menu.get_item(draw_row, draw_col)
+                if item and right_arrow_file:
+                    y_arrow = y0 + ((self.row_height - right_arrow.height)/2)
+                    x_arrow = x0 - right_arrow.width
+                    right_arrow_settings = (x_arrow, y_arrow, 
right_arrow.width, right_arrow.height)
+                    image = self.drawimage(right_arrow_file, 
right_arrow_settings )
+                    if image:
+                        self.objects.append(image)
+
+
+            except:
+                log.exception('grid')
+            y0 += self.row_height
+
+        # draw Up/down arrows
+        #todo

Modified: trunk/ui/src/gui/compat.py
==============================================================================
--- trunk/ui/src/gui/compat.py  (original)
+++ trunk/ui/src/gui/compat.py  Tue Oct  2 15:57:44 2007
@@ -52,7 +52,7 @@
 
 class _Menu(BaseApplication):
     name = 'menu'
-    areas = ('screen', 'title', 'subtitle', 'view', 'listing', 'info')
+    areas = ('screen', 'title', 'subtitle', 'view', 'listing', 'info', 'grid')
 
     def __init__(self):
         from freevo.ui.gui.areas import Handler

Modified: trunk/ui/src/gui/theme.py
==============================================================================
--- trunk/ui/src/gui/theme.py   (original)
+++ trunk/ui/src/gui/theme.py   Tue Oct  2 15:57:44 2007
@@ -533,12 +533,12 @@
 
 class MenuSet(object):
     """
-    the complete menu with the areas screen, title, subtitle, view, listing
-    and info in it
+    the complete menu with the areas screen, title, subtitle, view, listing,
+    grid and info in it
     """
     def __init__(self):
         self.areas = [ 'screen', 'title', 'subtitle', 'view', 'listing',
-                       'info', 'progress' ]
+                       'info', 'grid', 'progress' ]
         for c in self.areas:
             setattr(self, c, Area(c))
 
@@ -567,7 +567,7 @@
     def __init__(self, name, source=None):
         XMLData.__init__(self, self.VARS, source)
         self.name = name
-        if name == 'listing':
+        if name == 'listing' or 'grid':
             self.images = {}
         if not source:
             self.x = -1
@@ -593,7 +593,7 @@
             except TypeError:
                 pass
         for subnode in node.children:
-            if subnode.name == u'image' and self.name == 'listing':
+            if subnode.name == u'image' and self.name == 'listing' or 'grid':
                 label = attr_str(subnode, 'label', '')
                 if label:
                     if not label in self.images:
@@ -1246,6 +1246,10 @@
                         for image in s[i].listing.images:
                             foo = s[i].listing.images[image]
                             s[i].listing.images[image] = 
foo.prepare_copy(None, search_dirs, self.__images)
+                    if s[i] and hasattr(s[i], 'grid'):
+                        for image in s[i].grid.images:
+                            foo = s[i].grid.images[image]
+                            s[i].grid.images[image] = foo.prepare_copy(None, 
search_dirs, self.__images)
 
         # menu structures
         self.default_menu = {}
@@ -1292,6 +1296,11 @@
                             for image in sli:
                                 sli[image] = sli[image].prepare_copy(None, 
search_dirs,
                                                    self.__images)
+                        if s[i] and hasattr(s[i], 'grid'):
+                            sli = s[i].grid.images
+                            for image in sli:
+                                sli[image] = sli[image].prepare_copy(None, 
search_dirs,
+                                                   self.__images)
 
         # prepare popup style
         self.popup = layout[self.__popup]

Modified: trunk/ui/src/menu/__init__.py
==============================================================================
--- trunk/ui/src/menu/__init__.py       (original)
+++ trunk/ui/src/menu/__init__.py       Tue Oct  2 15:57:44 2007
@@ -40,5 +40,6 @@
 from mediaitem import MediaItem
 from action import Action
 from menu import Menu
+from gridmenu import GridMenu
 from stack import MenuStack
 from plugin import ItemPlugin, MediaPlugin

Added: trunk/ui/src/menu/gridmenu.py
==============================================================================
--- (empty file)
+++ trunk/ui/src/menu/gridmenu.py       Tue Oct  2 15:57:44 2007
@@ -0,0 +1,231 @@
+# -*- coding: iso-8859-1 -*-
+# -----------------------------------------------------------------------------
+# gridmenu.py - a page for the gridmenu stack
+# -----------------------------------------------------------------------------
+# $Id$
+#
+# -----------------------------------------------------------------------------
+# Freevo - A Home Theater PC framework
+# Copyright (C) 2007 Dirk Meyer, et al.
+#
+# First Edition: Joost <[EMAIL PROTECTED]>
+# Maintainer:    Dirk Meyer <[EMAIL PROTECTED]>
+#
+# Please see the file AUTHORS 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
+#
+# -----------------------------------------------------------------------------
+
+__all__ = [ 'GridMenu' ]
+
+# python imports
+import logging
+
+# kaa imports
+from kaa.weakref import weakref
+
+# freevo imports
+from freevo.ui import config
+from freevo.ui.event import *
+
+# menu imports
+from item import Item
+from menu import Menu
+
+# get logging object
+log = logging.getLogger()
+
+class GridMenu(Menu):
+    """
+    A Menu page with Items in grid form for the MenuStack. 
+    It is not allowed to change the selected item or the 
+    internal selection directly, use 'select' or 'set_items' 
+    to do this.
+    The grid is drawn row by row. The advanced_mode of this
+    menu allowes row items to have different width.
+    """
+    next_id = 0
+
+    def __init__( self, heading, grid = None, reload_func = None, type = None 
):
+        Menu.__init__(self, heading, reload_func = reload_func, type=type)
+        # Defines if the advanced mode is used
+        self.advanced_mode = False
+
+        # set items
+        self.selected_col = 0
+        self.selected_row = 0
+        self.base_col = 0
+        self.base_row = 0
+        self.last_base_col = -1
+        self.last_base_row = -1
+        self.update_view = True
+        self.set_items(grid, refresh=False)
+
+
+    def set_items(self, grid=None, selected=None, refresh=True):
+        """
+        Set/replace the items.
+        """
+        if grid is None:
+            grid = []
+
+        # delete ref to menu for old items
+        for c in self.choices:
+            for r in c:
+                if self.advanced_mode:
+                    r[1].menu = None
+                else:
+                    r.menu = None
+        # increase state variable
+        self.state += 1
+
+        # set new items and selection
+        self.choices = grid
+
+        # select given item
+        if selected is not None:
+            self.select(selected)
+
+        # try to reset selection in case we had one
+        if not self.selected:
+            # no old selection
+            if len(self.choices):
+                 self.select(col=0, row=0)
+
+        # set menu (self) pointer to the items
+        sref = weakref(self)
+        for c in self.choices:
+            for r in c:
+                # Check if advanced mode is used
+                if self.advanced_mode:
+                    r[1].menu = sref
+                else:
+                    r.menu = sref
+
+        if refresh and self.stack:
+            self.stack.refresh()
+
+
+    def select(self, item=None, col=0, row=0, refresh=True):
+        """
+        Set the selection to a specific item in the list. If item in an int
+        select a new item relative to current selected
+        """
+        if isinstance(item, Item):
+            # select item
+            for pos, row in enumerate(self.choices):
+                row_items = row
+                if self.advanced_mode:
+                    row_items = [ i[1] for i in row ]
+                if item in row_items:
+                    self.selected_col = row_items.index(item)
+                    self.selected_row = pos
+                    self.selected = item
+                    break
+            else:
+                log.error('%s not in list', item)
+                return False
+
+        elif item is None:
+            # select relative
+            self.selected_row = min(max(row, 0), len(self.choices)-1 )
+            self.selected_col = min(max(col, 0), 
len(self.choices[self.selected_row])-1 )
+            if self.advanced_mode:
+                self.selected = 
self.choices[self.selected_row][self.selected_col][1]
+            else:
+                self.selected = 
self.choices[self.selected_row][self.selected_col]
+
+        self.selected_id  = self.selected.__id__()
+
+        # Find Which columns/rows to draw, the next update
+        if self.selected_col-self.base_col > self.cols-1:
+            self.base_col = self.selected_col - (self.cols-1)
+        elif self.selected_col-self.base_col < 0:
+            self.base_col = self.selected_col
+    
+        if self.selected_row-self.base_row > self.rows-1:
+            self.base_row = self.selected_row - (self.rows-1)
+        elif self.selected_row-self.base_row < 0:
+            self.base_row = self.selected_row
+            
+        # refresh view?
+        if (self.last_base_col != self.base_col) or \
+           (self.last_base_row != self.base_row):
+            self.update_view = True
+
+        return True
+
+
+    def get_item(self, row, col):
+        """
+        Return the data for that col, row.
+        """
+        try:
+            return self.choices[self.base_row+row][self.base_col+col]
+        except (IndexError, KeyError):
+            return None
+
+
+    def get_column_name(self, col):
+        """
+        Return the column name
+        """
+        return _("Column %s") % (self.base_col+col)
+
+
+    def get_row_name(self, row):
+        """
+        Return the row name
+        """
+        return _("Row %s") % (self.base_row+row)
+
+
+    def eventhandler(self, event):
+        """
+        Handle events for this menu page.
+        """
+        if self.choices == None:
+            return False
+
+        if event == MENU_UP:
+            self.select(col=self.selected_col, row=self.selected_row-1 )
+            return True
+
+        if event == MENU_DOWN:
+            self.select(col=self.selected_col, row=self.selected_row+1 )
+            return True
+
+        if event == MENU_PAGEUP:
+            self.select(col=self.selected_col, row=self.selected_row-self.rows 
)
+            return True
+
+        if event == MENU_PAGEDOWN:
+            self.select(col=self.selected_col, row=self.selected_row+self.rows 
)
+            return True
+
+        if event == MENU_LEFT:
+            self.select(col=self.selected_col-1, row=self.selected_row )
+            return True
+
+        if event == MENU_RIGHT:
+            self.select(col=self.selected_col+1, row=self.selected_row )
+            return True
+
+        if event in (MENU_PLAY_ITEM, MENU_CHANGE_SELECTION, MENU_SELECT,
+                     MENU_PLAY_ITEM, MENU_SUBMENU, MENU_CALL_ITEM_ACTION):
+            return Menu.eventhandler(self, event)
+
+        return False

Modified: trunk/ui/src/menu/listing.py
==============================================================================
--- trunk/ui/src/menu/listing.py        (original)
+++ trunk/ui/src/menu/listing.py        Tue Oct  2 15:57:44 2007
@@ -49,7 +49,7 @@
     """
     A basic listing of items.
     """
-    def __init__(self, choices=[], selected=None):
+    def __init__(self, choices=None, selected=None):
         # state, will increase on every item change
         self.state = 0
 
@@ -58,7 +58,8 @@
         self.selected = None
         self.selected_id = None
         self.selected_pos = -1
-        self.set_items(choices, selected)
+        if choices is not None:
+            self.set_items(choices, selected)
 
 
 

Modified: trunk/ui/src/menu/menu.py
==============================================================================
--- trunk/ui/src/menu/menu.py   (original)
+++ trunk/ui/src/menu/menu.py   Tue Oct  2 15:57:44 2007
@@ -56,7 +56,7 @@
     """
     next_id = 0
 
-    def __init__(self, heading, choices=[], reload_func = None, type = None):
+    def __init__(self, heading, choices=None, reload_func = None, type = None):
         ItemList.__init__(self, choices)
 
         self.heading = heading

Modified: trunk/ui/src/menu/stack.py
==============================================================================
--- trunk/ui/src/menu/stack.py  (original)
+++ trunk/ui/src/menu/stack.py  Tue Oct  2 15:57:44 2007
@@ -45,7 +45,6 @@
 from freevo.ui.event import *
 
 # menu imports
-from menu import Menu
 from item import Item
 
 # get logging object

Modified: trunk/ui/src/tv/plugins/config.cxml
==============================================================================
--- trunk/ui/src/tv/plugins/config.cxml (original)
+++ trunk/ui/src/tv/plugins/config.cxml Tue Oct  2 15:57:44 2007
@@ -16,6 +16,10 @@
         </var>
     </group>
 
+    <group name="testguide" plugin="15">
+        <desc>Add item to show the test guide</desc>
+    </group>
+
     <group name="genre" plugin="30">
         <desc>Add item to browse the EPG by genre</desc>
     </group>

Added: trunk/ui/src/tv/plugins/testguide.py
==============================================================================
--- (empty file)
+++ trunk/ui/src/tv/plugins/testguide.py        Tue Oct  2 15:57:44 2007
@@ -0,0 +1,265 @@
+# -*- coding: iso-8859-1 -*-
+# -----------------------------------------------------------------------------
+# testguide.py - The the Freevo TV Guide
+# -----------------------------------------------------------------------------
+# $Id: testguide.py 9541 2007-05-01 18:46:35Z dmeyer $
+#
+# -----------------------------------------------------------------------------
+# Freevo - A Home Theater PC framework
+# Copyright (C) 2002 Krister Lagerstrom, 2003-2007 Dirk Meyer, et al.
+#
+# Maintainer:    Dirk Meyer <[EMAIL PROTECTED]>
+#
+# Please see the file AUTHORS 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
+#
+# -----------------------------------------------------------------------------
+
+
+# python imports
+import os
+import sys
+import time
+import logging
+
+import kaa.epg
+import kaa.notifier
+
+# freevo imports
+from freevo.ui.event import *
+from freevo.ui.mainmenu import MainMenuPlugin
+from freevo.ui.menu import Menu, GridMenu, ActionItem
+from freevo.ui.tv.program import ProgramItem
+from freevo.ui.application import MessageWindow
+from freevo.ui import config
+
+# get logging object
+log = logging.getLogger('tv')
+
+ONE_HOUR_IN_TIME = (60 * 60)
+ONE_DAY_IN_TIME = (24 * ONE_HOUR_IN_TIME)
+COLUMN_TIME = ONE_HOUR_IN_TIME
+
+#fix me: Use number of days from config files
+MAX_DAYS = 1
+
+class PluginInterface(MainMenuPlugin):
+
+    def items(self, parent):
+        return [ ActionItem(_('TV Guide'), parent, self.show) ]
+
+    def show(self, parent):
+        if not kaa.epg.is_connected():
+            MessageWindow(_('TVServer not running')).show()
+            return
+        guide = TVGuide2(parent)
+        parent.get_menustack().pushmenu(guide)
+
+    
+class TVGuide2(GridMenu):
+    """
+    TVGuide2 menu.
+    """
+    def __init__(self, parent):
+        GridMenu.__init__(self, _('TV Guide2'), type = 'tv grid')
+        self.parent = parent
+        self.viewed_time = int(time.time())
+        self.viewed_time = ((self.viewed_time / COLUMN_TIME) * COLUMN_TIME)
+        self.prev_viewed_time = 0
+        
+        # current channel is the first one
+        self.channels = kaa.epg.get_channels(sort=True)
+        # FIXME: make it work without step()
+        if isinstance(self.channels, kaa.notifier.InProgress):
+            while not self.channels.is_finished:
+                kaa.notifier.step()
+            self.channels = self.channels()
+
+        self.query_start_time = self.viewed_time
+        self.query_stop_time = 0
+        self.query_data = []
+        # current program is the current running
+        self.advanced_mode = True
+        self.selected = None
+        self.selected_start_time = self.viewed_time
+        self.update()
+
+        
+    def get_item(self, row, col):
+        """
+        Return the data for that col, row.
+        """
+        try:
+            item = self.grid[self.base_row+row][col]
+        except:
+            return None
+        return item
+
+        
+    @kaa.notifier.yield_execution()
+    def update(self):
+        """
+        update the guide information
+        """
+        #Find the new time to display
+        if self.selected:
+            self.selected_start_time = self.selected.start
+            if self.selected_start_time >= (self.viewed_time + (COLUMN_TIME * 
self.cols)):
+                #Put halfway the grid
+                self.viewed_time = self.selected_start_time - (COLUMN_TIME * 
self.cols / 2)
+            elif self.selected_start_time < self.viewed_time:
+                self.viewed_time = self.selected_start_time
+                
+            self.viewed_time = ((self.viewed_time / COLUMN_TIME) * COLUMN_TIME)
+            
+        #do we need to start a new query?
+        if self.viewed_time < self.query_start_time or \
+           (self.viewed_time + (COLUMN_TIME * self.cols)) > 
self.query_stop_time:
+            print 'new query'
+            self.query_start_time = self.viewed_time
+            self.query_stop_time = self.query_start_time + ONE_DAY_IN_TIME
+        
+            self.query_data = []
+            for channel in self.channels:
+                programs = []
+                # query the epg database in background
+                wait = kaa.epg.search(channel=channel, 
time=(self.query_start_time, self.query_stop_time))
+                yield wait
+                # get data from InProgress object
+                query_data = wait()
+                #Sort the programs
+                query_data.sort(lambda x,y: cmp(x.start, y.start))
+                for data in query_data:
+                    data = ProgramItem(data, self)
+                    programs.append(data)
+        
+                self.query_data.append(programs)
+
+        #Calculate new view
+        if self.prev_viewed_time != self.viewed_time:
+            self.update_view = True
+            self.prev_viewed_time = self.viewed_time
+            items = []
+            for channel in self.query_data:
+                programs = []
+                for data in channel:
+                    #only add items which are in the view
+                    if data.stop >= self.viewed_time:
+                        #selected the correct first item
+                        if self.selected == None and \
+                           data.stop > self.viewed_time:
+                            self.selected = data
+                        start = max(data.start, self.viewed_time)
+                        size = ((data.stop - start) *100) / COLUMN_TIME
+                        i = (size , data)
+                        programs.append(i)
+    
+                items.append(programs)
+        
+            self.set_items(items, selected=self.selected)
+
+
+    def get_column_name(self, col):
+        """
+        Return the column name
+        """
+        #get rid of the minutes
+        t = self.viewed_time
+        t += (col*COLUMN_TIME)
+        return unicode(time.strftime(config.tv.timeformat,
+                       time.localtime(t)))
+
+    def get_row_name(self, row):
+        """
+        Return the row name
+        """
+        return self.channels[self.base_row+row].name
+
+    def select_program(self):
+        """
+        Select program for the new row
+        """
+        for program in self.grid[self.selected_row]:
+            size, data = program
+            if data.start <= self.selected_start_time and data.stop > 
self.selected_start_time:
+                self.select(row=self.selected_row, 
col=self.grid[self.selected_row].index(program))
+
+    def eventhandler(self, event):
+        handled = False
+        if not self.selected:
+            # not ready yet
+            return True
+
+        if event == MENU_CHANGE_STYLE:
+            handled = True
+
+        elif event == MENU_UP:
+            self.select(col=self.selected_col,
+                        row=self.selected_row-1 )
+            self.select_program()
+            handled = True
+
+        elif event == MENU_DOWN:
+            self.select(col=self.selected_col,
+                        row=self.selected_row+1 )
+            self.select_program()
+            handled = True
+
+        elif event == MENU_LEFT:
+            self.select(col=self.selected_col-1,
+                        row=self.selected_row )
+            self.update()
+            handled = True
+
+        elif event == MENU_RIGHT:
+            self.select(col=self.selected_col+1,
+                        row=self.selected_row )
+            self.update()
+            handled = True
+
+        elif event == TV_SHOW_CHANNEL:
+            self.selected.channel_details()
+            handled = True
+
+        elif event == MENU_SUBMENU:
+            self.selected.submenu(additional_items=True)
+            handled = True
+
+        elif event == TV_START_RECORDING:
+            # TODO: make this schedule or remove
+            self.selected.submenu(additional_items=True)
+            handled = True
+
+        elif event == PLAY:
+            self.selected.watch_channel()
+            handled = True
+
+        elif event == MENU_SELECT or event == PLAY:
+            # Check if the selected program is >7 min in the future
+            # if so, bring up the submenu
+            now = time.time() + (7*60)
+            if self.selected.start > now:
+                self.selected.submenu(additional_items=True)
+            else:
+                self.selected.watch_channel()
+            handled = True
+
+        else:
+            #If not handled try default eventhandler
+            handled = GridMenu.eventhandler(self, event)
+
+        return handled
+ 
\ No newline at end of file

-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2005.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
_______________________________________________
Freevo-cvslog mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/freevo-cvslog

Reply via email to