Author: dmeyer
Date: Mon Feb 19 20:06:25 2007
New Revision: 9248
Added:
trunk/ui/src/menu/listing.py
Modified:
trunk/ui/src/audio/audiodiskitem.py
trunk/ui/src/directory.py
trunk/ui/src/menu/__init__.py
trunk/ui/src/menu/menu.py
trunk/ui/src/playlist.py
Log:
Move listing handling code from Menu to new class ItemList. Playlist
is now also an ItemList which makes it possible to share some code
and a future kaa.candy based gui widget can draw both.
Modified: trunk/ui/src/audio/audiodiskitem.py
==============================================================================
--- trunk/ui/src/audio/audiodiskitem.py (original)
+++ trunk/ui/src/audio/audiodiskitem.py Mon Feb 19 20:06:25 2007
@@ -76,7 +76,7 @@
# add all playable items to the playlist of the directory
# to play one files after the other
- self.playlist = play_items
+ self.set_playlist(play_items)
# all items together
items = []
Modified: trunk/ui/src/directory.py
==============================================================================
--- trunk/ui/src/directory.py (original)
+++ trunk/ui/src/directory.py Mon Feb 19 20:06:25 2007
@@ -360,7 +360,6 @@
"""
# FIXME: check for password
- self.playlist = []
play_items = []
dir_items = []
pl_items = []
@@ -470,7 +469,7 @@
# add all playable items to the playlist of the directory
# to play one files after the other
if self['config:isplaylist']:
- self.playlist = play_items
+ self.set_playlist(play_items)
# build a list of all items
items = dir_items + pl_items + play_items
Modified: trunk/ui/src/menu/__init__.py
==============================================================================
--- trunk/ui/src/menu/__init__.py (original)
+++ trunk/ui/src/menu/__init__.py Mon Feb 19 20:06:25 2007
@@ -32,6 +32,7 @@
# import the submodules
from files import Files
from item import Item
+from listing import ItemList
from mediaitem import MediaItem
from action import Action
from menu import Menu
Added: trunk/ui/src/menu/listing.py
==============================================================================
--- (empty file)
+++ trunk/ui/src/menu/listing.py Mon Feb 19 20:06:25 2007
@@ -0,0 +1,158 @@
+# -*- coding: iso-8859-1 -*-
+# -----------------------------------------------------------------------------
+# listing.py - a simple item listing
+# -----------------------------------------------------------------------------
+# $Id$
+#
+# The file holds the base class for an item listing. It is used for the
+# Menu and for Playlist items. One base class makes it possible to share
+# some code between the two classes and a future gui widget for kaa.candy
+# can be created to draw both.
+#
+# -----------------------------------------------------------------------------
+# Freevo - A Home Theater PC framework
+# Copyright (C) 2007 Dirk Meyer, et al.
+#
+# First Edition: Dirk Meyer <[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__ = [ 'ItemList' ]
+
+# python imports
+import logging
+
+# menu imports
+from item import Item
+
+# get logging object
+log = logging.getLogger()
+
+class ItemList(object):
+ """
+ A basic listing of items.
+ """
+ def __init__(self, choices=[], selected=None):
+ # state, will increase on every item change
+ self.state = 0
+
+ # set items
+ self.choices = []
+ self.selected = None
+ self.selected_id = None
+ self.selected_pos = -1
+ self.set_items(choices, selected)
+
+
+
+ def set_items(self, items, selected=None):
+ """
+ Set/replace the items.
+ """
+ # increase state variable
+ self.state += 1
+
+ # set new choices and selection
+ self.choices = items
+
+ # select given item
+ if selected is not None:
+ return self.select(selected)
+
+ # try to reset selection in case we had one
+ if not self.selected:
+ # no old selection
+ if len(self.choices):
+ return self.select(self.choices[0])
+ return self.select(None)
+
+ if self.selected in self.choices:
+ # item is still there, reuse it
+ return self.select(self.selected)
+
+ for c in self.choices:
+ if c.__id__() == self.selected_id:
+ # item with the same id is there, use it
+ return self.select(c)
+ if self.choices:
+ # item is gone now, try to the selection close
+ # to the old item
+ pos = max(0, min(self.selected_pos-1, len(self.choices)-1))
+ return self.select(self.choices[pos])
+ # no item in the list
+ return self.select(None)
+
+
+ def select(self, item):
+ """
+ Set the selection to a specific item in the list. If item in an int
+ select a new item relative to current selected
+ """
+ if self.selected == item:
+ # nothing changed
+ return True
+
+ if not self.choices or item is None:
+ # We have no choices and can't select. This could happen when
+ # a DirItem is no playing playlist
+ self.selected = None
+ self.selected_pos = -1
+ return True
+
+ if isinstance(item, Item):
+ # select item
+ if not item in self.choices:
+ log.error('%s not in list', item)
+ ItemList.select(self, self.choices[0])
+ return False
+ self.selected = item
+ self.selected_pos = self.choices.index(item)
+ self.selected_id = self.selected.__id__()
+ return True
+ # select relative
+ p = min(max(0, self.selected_pos + item), len(self.choices) - 1)
+ return ItemList.select(self, self.choices[p])
+
+
+ def get_items(self):
+ """
+ Return the list of items.
+ """
+ return self.choices
+
+
+ def get_selection(self):
+ """
+ Return current selected item.
+ """
+ return self.selected
+
+
+ def change_item(self, old, new):
+ """
+ Replace the item 'old' with the 'new'.
+ """
+ # increase state variable
+ self.state += 1
+
+ # change item
+ self.choices[self.choices.index(old)] = new
+ if self.selected == old:
+ self.select(new)
Modified: trunk/ui/src/menu/menu.py
==============================================================================
--- trunk/ui/src/menu/menu.py (original)
+++ trunk/ui/src/menu/menu.py Mon Feb 19 20:06:25 2007
@@ -43,11 +43,12 @@
# menu imports
from item import Item
+from listing import ItemList
# get logging object
log = logging.getLogger()
-class Menu(object):
+class Menu(ItemList):
"""
A Menu page with Items for the MenuStack. It is not allowed to change
the selected item or the internal choices directly, use 'select',
@@ -56,6 +57,7 @@
next_id = 0
def __init__(self, heading, choices=[], reload_func = None, type = None):
+ ItemList.__init__(self, choices)
self.heading = heading
self.stack = None
@@ -63,16 +65,9 @@
# unique id of the menu object
Menu.next_id += 1
self.id = Menu.next_id
- # state, will increase on every item change
- self.state = 0
# position in the menu stack
self.pos = -1
- # set items
- self.choices = []
- self.selected = None
- self.set_items(choices, False)
-
# special items for the new skin to use in the view or info
# area. If None, menu.selected will be taken
self.infoitem = None
@@ -103,105 +98,27 @@
Set/replace the items in this menu. If refresh is True, the menu
stack will be refreshed and redrawn.
"""
- # increase state variable
- self.state += 1
-
# delete ref to menu for old choices
for c in self.choices:
c.menu = None
# set new choices and selection
- self.choices = items
+ ItemList.set_items(self, items)
# set menu (self) pointer to the items
sref = weakref(self)
for c in self.choices:
c.menu = sref
- # try to reset selection in case we had one
- if not self.selected:
- # no old selection
- if len(self.choices):
- self.select(self.choices[0])
- else:
- self.select(None)
-
- elif self.selected in self.choices:
- # item is still there, reuse it
- self.select(self.selected)
-
- else:
- for c in self.choices:
- if c.__id__() == self.selected_id:
- # item with the same id is there, use it
- self.select(c)
- break
- else:
- if self.choices:
- # item is gone now, try to the selection close
- # to the old item
- pos = max(0, min(self.selected_pos-1, len(self.choices)-1))
- self.select(self.choices[pos])
- else:
- # no item in the list
- self.select(None)
-
if refresh and self.stack:
self.stack.refresh()
- def select(self, item):
- """
- 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
- self.selected = item
- try:
- self.selected_pos = self.choices.index(item)
- self.selected_id = self.selected.__id__()
- except ValueError, e:
- log.exception('crash by select %s in %s' % (item,
self.choices))
- if self.choices:
- self.select(self.choices[0])
- else:
- self.select(None)
- elif item == None:
- # nothing to select
- self.selected = None
- self.selected_pos = -1
- else:
- # select relative
- p = min(max(0, self.selected_pos + item), len(self.choices) - 1)
- self.select(self.choices[p])
-
-
- def get_items(self):
- """
- Return the list of items in this menu.
- """
- return self.choices
-
-
- def get_selection(self):
- """
- Return current selected item.
- """
- return self.selected
-
-
def change_item(self, old, new):
"""
Replace the item 'old' with the 'new'.
"""
- # increase state variable
- self.state += 1
-
- # change item
- self.choices[self.choices.index(old)] = new
- if self.selected == old:
- self.select(new)
+ ItemList.change_item(self, old, new)
old.menu = None
new.menu = weakref(self)
Modified: trunk/ui/src/playlist.py
==============================================================================
--- trunk/ui/src/playlist.py (original)
+++ trunk/ui/src/playlist.py Mon Feb 19 20:06:25 2007
@@ -48,7 +48,7 @@
import fxditem
from event import *
-from menu import Action, Item, MediaItem, Menu, MediaPlugin
+from menu import Action, Item, MediaItem, Menu, MediaPlugin, ItemList
# get logging object
log = logging.getLogger()
@@ -57,7 +57,7 @@
REPEAT_ITEM = 1
REPEAT_PLAYLIST = 2
-class Playlist(MediaItem):
+class Playlist(MediaItem, ItemList):
"""
Class for playlists. A playlist can be created with a list of items, a
filename containing the playlist or a (list of) beacon query(s).
@@ -74,30 +74,32 @@
3) a list (directoryname, recursive=0|1)
"""
MediaItem.__init__(self, parent, type='playlist')
+ ItemList.__init__(self)
+
self.name = str_to_unicode(name)
# variables only for Playlist
- self.playlist = playlist
+ self._playlist = playlist
self.autoplay = autoplay
self.repeat = repeat
self.display_type = type
self.next_pos = None
- self.__playlist_valid = False
+ self._playlist_valid = False
self.background_playlist = None
- self.random = random
-
- # Listing stuff, this makes it look like a Menu to the listing
- # widget. That needs to be cleaned up, e.g. make a List class and
- # let the listing widget check for it.
- self.state = 0
- self.selected = None
- self.selected_pos = None
+ self._random = random
# create a basic info object
self.info = {}
+ def set_playlist(self, playlist):
+ """
+ Set a new playlist.
+ """
+ self.set_items(playlist, 0)
+
+
def _read_m3u(self, plsname, content):
"""
This is the (m3u) playlist reading function.
@@ -137,86 +139,88 @@
return playlist
- def __create_playlist_items(self):
+ def _playlist_create_items(self):
"""
Build the playlist. Create a list of items and filenames. This function
will load the playlist file or expand directories
"""
- if self.__playlist_valid:
- # we called this function before
- return
- self.__playlist_valid = True
+ self._playlist_valid = True
# create a basic info object
self.info = {}
- playlist = self.playlist
- self.playlist = []
+ items = []
- if isinstance(playlist, (str, unicode)):
+ if isinstance(self._playlist, (str, unicode)):
# playlist is a filename, load the file and create playlist
- self.set_url(playlist)
- log.info('create playlist for %s' % playlist)
+ self.set_url(self._playlist)
+ log.info('create playlist for %s' % self._playlist)
try:
- f=open(playlist, "r")
+ f=open(self._playlist, "r")
content = map(lambda l: l.strip(' \n\r'), f.readlines())
f.close
if content and content[0].find("[playlist]") > -1:
- playlist = self._read_pls(playlist, content)
+ self._playlist = self._read_pls(self._playlist, content)
else:
- playlist = self._read_m3u(playlist, content)
+ self._playlist = self._read_m3u(self._playlist, content)
except (OSError, IOError), e:
log.error('playlist error: %s' % e)
- playlist = []
+ self._playlist = []
- if isinstance(playlist, kaa.beacon.Query):
- playlist = [ playlist ]
+ if isinstance(self._playlist, kaa.beacon.Query):
+ self._playlist = [ self._playlist ]
# Note: playlist is a list of Items, strings (filenames) or a
# beacon queries now.
plugins = MediaPlugin.plugins(self.display_type)
- for item in playlist:
+ for item in self._playlist:
if isinstance(item, Item):
# Item object, correct parent
item = copy.copy(item)
item.parent = weakref(self)
- self.playlist.append(item)
+ items.append(item)
continue
if not isinstance(item, kaa.beacon.Query):
# make item a beacon query
item = kaa.beacon.query(filename=item)
- _playlist = []
- items = item.get(filter='extmap')
+ playlist = []
+ fitems = item.get(filter='extmap')
for p in plugins:
- _playlist.extend(p.get(self, items))
+ playlist.extend(p.get(self, fitems))
# sort beacon query on url
- _playlist.sort(lambda x,y: cmp(x.url, y.url))
+ playlist.sort(lambda x,y: cmp(x.url, y.url))
# add to playlist
- self.playlist.extend(_playlist)
+ items.extend(playlist)
+ self.set_items(items, 0)
+ self._playlist = []
- def randomize(self):
+ def _randomize(self):
"""
resort the playlist by random
"""
- old = self.playlist
- self.playlist = []
- while old:
- element = random.choice(old)
- old.remove(element)
- self.playlist += [ element ]
+ if not self._random:
+ return False
+ playlist = self.choices
+ randomized = []
+ while playlist:
+ element = random.choice(playlist)
+ playlist.remove(element)
+ randomized.append(element)
+ self.set_items(randomized, 0)
+ return True
def __getitem__(self, attr):
"""
return the specific attribute
"""
- if not self.__playlist_valid:
- self.__create_playlist_items()
+ if not self._playlist_valid:
+ self._playlist_create_items()
return MediaItem.__getitem__(self, attr)
@@ -224,8 +228,8 @@
"""
return the actions for this item: play and browse
"""
- if not self.__playlist_valid:
- self.__create_playlist_items()
+ if not self._playlist_valid:
+ self._playlist_create_items()
browse = Action(_('Browse Playlist'), self.browse)
play = Action(_('Play'), self.play)
@@ -235,8 +239,8 @@
else:
items = [ browse, play ]
- if not self.random:
- items.append(Action(_('Random play all items'), self.random_play))
+ if not self._random:
+ items.append(Action(_('Random play all items'), self._play_random))
return items
@@ -245,65 +249,58 @@
"""
show the playlist in the menu
"""
- if not self.__playlist_valid:
- self.__create_playlist_items()
- if self.random:
- self.randomize()
+ if not self._playlist_valid:
+ self._playlist_create_items()
+
+ # randomize if needed
+ self._randomize()
display_type = self.display_type
if self.display_type == 'tv':
display_type = 'video'
- menu = Menu(self.name, self.playlist, type = display_type)
+ menu = Menu(self.name, self.choices, type = display_type)
self.pushmenu(menu)
- def random_play(self):
- """
- play the playlist in random order
- """
- Playlist(playlist=self.playlist, parent=self.parent,
- type=self.display_type, random=True,
- repeat=self.repeat).play()
-
-
def play(self):
"""
play the playlist
"""
- if not self.playlist:
+ if not self._playlist_valid:
+ self._playlist_create_items()
+
+ if not self.choices:
log.warning('empty playlist')
return False
- # First start playing. This is a bad hack and needs to
- # be fixed when fixing the whole self.playlist stuff
-
- if not self.__playlist_valid:
- self.__create_playlist_items()
-
- if self.random:
- self.randomize()
+ # randomize if needed
+ self._randomize()
if self.background_playlist:
self.background_playlist.play()
- if not self.playlist:
- log.warning('empty playlist')
- return False
-
- # XXX looks like a menu now
- self.choices = self.playlist
-
# FIXME: add random code
self.next_pos = 0
- self.state += 1
# Send a PLAY_START event for ourself
PLAY_START.post(self)
self._play_next()
+ def _play_random(self):
+ """
+ play the playlist in random order
+ """
+ Playlist(playlist=self.choices, parent=self.parent,
+ type=self.display_type, random=True,
+ repeat=self.repeat).play()
+
+
def _play_next(self):
+ """
+ Play the next item (defined by self.next_pos).
+ """
self.select(self.choices[self.next_pos])
if hasattr(self.selected, 'play'):
@@ -338,23 +335,13 @@
"""
Select item that is playing right now.
"""
- if not self.playlist:
- # no need to change stuff (video dir playing)
- return True
-
- if not hasattr(self, 'choices') or self.choices != self.playlist:
- self.choices = self.playlist
- self.state += 1
-
- if self.selected == item:
- return True
-
- self.selected_pos = self.choices.index(item)
- self.selected = item
+ ItemList.select(self, item)
+ if self.selected is None:
+ return False
- if item.menu and item in item.menu.choices:
+ if self.selected.menu and self.selected in self.selected.menu.choices:
# update menu
- item.menu.select(item)
+ self.selected.menu.select(self.selected)
# get next item
self.next_pos = (self.selected_pos+1) % len(self.choices)
@@ -373,22 +360,20 @@
"""
Handle playlist specific events
"""
- if event == PLAY_START and event.arg in self.playlist:
- # FIXME: remove this after application update
- self.select(event.arg)
+ if event == PLAY_START:
# a new item started playing, cache next (if supported)
if self.next_pos is not None and \
hasattr(self.choices[self.next_pos], 'cache'):
self.choices[self.next_pos].cache()
return True
- # give the event to the next eventhandler in the list
if not self.selected:
+ # There is no selected item. All following functions need
+ # with self.selected so pass the event to the parent now.
if event == PLAY_END:
event = Event(PLAY_END, self)
return MediaItem.eventhandler(self, event)
-
if event == PLAYLIST_TOGGLE_REPEAT:
self.repeat += 1
if self.repeat == REPEAT_ITEM:
@@ -401,7 +386,6 @@
OSD_MESSAGE.post(arg)
return True
-
if event == PLAY_END:
if self.repeat == REPEAT_ITEM:
# Repeat current item
@@ -410,11 +394,15 @@
if self.next_pos is not None:
# Play next item
self._play_next()
- else:
- # Nothing to play
- self.stop()
- # Send a PLAY_END event for ourself
- PLAY_END.post(self)
+ return True
+ # Nothing to play
+ self.selected = None
+ # Call stop() again here. We need that to stop the bg playlist
+ # but it is not good because stop() is called from the outside
+ # and will result in this PLAY_END.
+ self.stop()
+ # Send a PLAY_END event for ourself
+ PLAY_END.post(self)
return True
if event == PLAYLIST_NEXT:
@@ -423,10 +411,8 @@
# current one sends the stop event
self.selected.stop()
return True
- else:
- # No next item
- OSD_MESSAGE.post(_('No Next Item In Playlist'))
-
+ # No next item
+ OSD_MESSAGE.post(_('No Next Item In Playlist'))
if event == PLAYLIST_PREV:
if self.selected_pos:
@@ -435,9 +421,8 @@
self.next_pos = self.selected_pos - 1
self.selected.stop()
return True
- else:
- # No previous item
- OSD_MESSAGE.post(_('no previous item in playlist'))
+ # No previous item
+ OSD_MESSAGE.post(_('no previous item in playlist'))
if event == STOP:
# Stop playing and send event to parent item
-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys-and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
Freevo-cvslog mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/freevo-cvslog