""" FeedItems.py
Straw module for handling items that belongs to a feed. This modules is
responsible for adding,cutting, and deleting items of the current feed.
"""
__copyright__ = "Copyright (c) 2002-2005 Free Software Foundation, Inc."
__license__ = """GNU General Public License
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 MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA. """
import QueueDict
import Config
import Feed
import Event
import ItemStore
import error
import locale
class FeedItems(object):
def __init__(self, feed):
self._feed = feed
self._items = QueueDict.ItemQueue()
self._init_items()
config = Config.get_instance()
config.signal_connect(Event.ItemOrderChangedSignal, self.item_order_changed)
config.signal_connect(Event.NumberOfItemsStoredChangedSignal, self.items_stored_changed)
def _init_items(self):
config = Config.get_instance()
self._items.clear()
self._items.sort_order = config.item_order
self._prefs_items_stored = config.number_of_items_stored
self._idseq = 0
self._number_of_items = None
self._loaded = False
@apply
def number_of_unread():
doc = ""
def fget(self):
return self._feed.n_items_unread
def fset(self, n):
self._feed.n_items_unread = n
return property(**locals())
@apply
def number_of_items():
doc = ""
def fget(self):
if self._number_of_items:
return self._number_of_items
if self._loaded:
error.log("was loaded but number_of_items was None!")
return 0
def fset(self, n):
self._number_of_items = n
return property(**locals())
def item_order_changed(self, event):
self._items.sort_order = event.sender.item_order
self._feed.signal_refresh_display()
def items_stored_changed(self, event):
self._prefs_items_stored = event.sender.number_of_items_stored
self._feed.signal_refresh_display()
def add_items(self, new_items):
cutpoint = self._get_cutpoint()
items = sorted(new_items, cmp=self._cmp)
newitems = []
removed_items = []
for x, item in enumerate(items):
if item in self._items:
continue
if not item.id:
self._idseq += 1
item.id = self._idseq
item.feed = self._feed
if self._number_of_items < cutpoint:
self._items.append(item)
else:
# no point on increasing the number of items here since it's
# already over the cutpoint
ritem = self._items.replace(item)
removed_items.append(ritem)
newitems.append(item)
item.signal_connect(Event.ItemReadSignal, self.item_read)
item.signal_connect(Event.ItemStickySignal, self._feed.forward_signal)
self._idseq = max(self._items.keys())
self.number_of_unread = len([i for i in self._items.itervalues() if not i.seen])
self.number_of_items = len(self._items)
if removed_items:
self._feed.signal_deleted_item(removed_items)
if newitems:
self._feed.signal_new_items(newitems)
self._feed.signal_refresh_display()
def restore_items(self, items):
"""
Restores the given items in an ordered form
# should we check for duplicates here? Or should that be done
# when new items arrive?
"""
items = self._sort(items)
no_restore = []
cutpoint = self._get_cutpoint()
for x,item in enumerate(items):
item.feed = self._feed
if item.sticky or x <= cutpoint:
item.signal_connect(Event.ItemReadSignal, self.item_read)
item.signal_connect(Event.ItemStickySignal, self._feed.forward_signal)
self._items.append(item)
continue
item.clean_up()
no_restore.append(item)
self._idseq = max(self._items.keys())
self.number_of_unread = len([i for i in self._items.itervalues() if not i.seen])
self.number_of_items = len(self._items)
if no_restore:
self._feed.signal_deleted_item(no_restore)
def _disconnect_item_signals(self, item):
item.signal_disconnect(Event.ItemReadSignal, self.item_read)
item.signal_disconnect(Event.ItemStickySignal, self._feed.forward_signal)
def item_read(self, signal):
if signal.sender.seen:
change = -1
else:
change = 1
self.number_of_unread = self.number_of_unread + change
self._feed.forward_signal(signal)
def get_items(self):
if not self._loaded:
self.load()
return self._items.values()
def get_item_index(self, item):
if not self._loaded:
self.load()
idx = self._items.index(item.id)
return idx
def mark_all_read(self):
if not self._loaded:
self.load()
keys = self._items.keys()
changed = [(keys.index(key), value) for key, value in self._items if value.set_seen_quiet()]
self.number_of_unread = 0
self._feed.signal_all_items_read(changed)
return
def load(self):
"""
Loads and restores the items from the data store
"""
if self._loaded:
return False
itemstore = ItemStore.get_instance()
items = itemstore.read_feed_items(self._feed)
if items:
#error.log("load ", self._feed.title, ", number of items: ", len(items), ", number of unread before restore: ", self.number_of_unread)
self.restore_items(items)
self._loaded = True
else:
#print "No items, not loading ", self._feed.title
self._loaded = False
return self._loaded
def unload(self):
"""
Unloads the items by disconnecting the signals and reinitialising the
instance variables
TODO: unload seems to lose some circular references. garbage collector
will find them, though, so maybe it's not a problem.
"""
if not self._loaded:
return
for key, item in self._items:
self._disconnect_item_signals(item)
self._init_items()
def _get_cutpoint(self):
"""
Returns the current cutpoint
"""
if self._feed.number_of_items_stored == Feed.Feed.DEFAULT:
cutpoint = self._prefs_items_stored
else:
cutpoint = self._feed.number_of_items_stored
return cutpoint
def _sort(self, items):
"""
Sorts the given items according to the sort order
"""
items.sort(self._cmp)
if self._items.sort_order:
items.reverse()
return items
def _cmp(self, a, b):
"""
Comparator method to compare items based on the item's pub_date attribute
If an item doesn't have a pub_date, it uses title and prioritizes the
unread items
"""
try:
return cmp(a.pub_date, b.pub_date)
except AttributeError:
return locale.strcoll(a.title, b.title) and not a.seen or not b.seen
syntax highlighted by Code2HTML, v. 0.9.1