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