+++ /dev/null
-# -*- coding: utf-8 -*-
-#######################################################################################
-# Plinn - http://plinn.org #
-# Copyright (C) 2005-2007 Benoît PIN <benoit.pin@ensmp.fr> #
-# #
-# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #
-#######################################################################################
-""" Plinn implementation of CMFBTree
-
-
-
-"""
-
-
-from Products.BTreeFolder2.BTreeFolder2 import BTreeFolder2Base
-from Products.ZCatalog.Lazy import LazyMap
-from BTrees.IOBTree import IOBTree
-from BTrees.OIBTree import OIBTree
-from Folder import PlinnFolder
-from zope.event import notify
-try :
- from zope.app.container.contained import notifyContainerModified
-except ImportError :
- ## Zope-2.13 compat
- from zope.container.contained import notifyContainerModified
-from events import ObjectPositionModified
-from zope.component.factory import Factory
-from Products.CMFCore.permissions import AddPortalFolders, \
- ManageProperties, \
- AccessContentsInformation
-from AccessControl import ClassSecurityInfo
-from Globals import InitializeClass
-from types import StringType
-
-
-class HugePlinnFolder(BTreeFolder2Base, PlinnFolder) :
- """ Plinn Folder for large set of objects
- """
-
- security = ClassSecurityInfo()
-
- __getitem__ = PlinnFolder.__getitem__
-
- def __init__(self, id, title='') :
- PlinnFolder.__init__(self, id, title)
- BTreeFolder2Base.__init__(self, id)
-
- def _initBTrees(self):
- super(HugePlinnFolder, self)._initBTrees()
- self._pos2id_index = IOBTree()
- self._id2pos_index = OIBTree()
-
- def _checkId(self, id, allow_dup=0) :
- PlinnFolder._checkId(self, id, allow_dup)
- BTreeFolder2Base._checkId(self, id, allow_dup)
-
- security.declareProtected(AddPortalFolders, 'manage_addHugePlinnFolder')
- def manage_addHugePlinnFolder(self, id, title='', REQUEST=None) :
- """ Add new a new HugePlinnFolder object with id *id*.
- """
- ob = HugePlinnFolder(id, title)
- self._setObject(id, ob)
- if REQUEST is not None :
- return self.folder_contents(self, REQUEST, portal_status_message='Folder added')
-
- def _setOb(self, id, object):
- super(HugePlinnFolder, self)._setOb(id, object)
- pos = self.objectCount() - 1
- self._pos2id_index[pos] = id
- self._id2pos_index[id] = pos
-
- def _delOb(self, id):
- pos = self._id2pos_index[id]
- self._id2pos_index.pop(id)
-
- for p in xrange(pos+1, self.objectCount()) :
- ident = self._pos2id_index[p]
- self._pos2id_index[p-1] = ident
- self._id2pos_index[ident] = p-1
-
- self._pos2id_index.pop(self.objectCount()-1)
-
- super(HugePlinnFolder, self)._delOb(id)
-
- security.declareProtected(AccessContentsInformation, 'objectIds')
- def objectIds(self, spec=None) :
- if spec is not None :
- return super(HugePlinnFolder, self).objectIds(spec)
-
- pos2id = lambda pos : self._pos2id_index[pos]
- return LazyMap(pos2id, xrange(self.objectCount()))
-
-
-
- security.declareProtected(ManageProperties, 'moveObjectsByDelta')
- def moveObjectsByDelta(self, ids, delta, subset_ids=None,
- suppress_events=False):
- """ Move specified sub-objects by delta.
- """
- if isinstance(ids, StringType):
- ids = (ids,)
-
- id2pos = self._id2pos_index
- pos2id = self._pos2id_index
- for id in ids :
- oldPosition = id2pos[id]
- newPosition = max(oldPosition + delta, 0)
-
- shift = delta > 0 and 1 or -1
- for p in xrange(oldPosition, newPosition, shift) :
- ident = pos2id[p+shift]
- pos2id[p] = ident
- id2pos[ident] = p
- if not suppress_events :
- notify(ObjectPositionModified(self[ident], self, p))
-
- id2pos[id] = newPosition
- pos2id[newPosition] = id
- if not suppress_events :
- notify(ObjectPositionModified(self[id], self, newPosition))
-
- if not suppress_events :
- notifyContainerModified(self)
-
- security.declareProtected(ManageProperties, 'moveObjectsAfter')
- def moveObjectsAfter(self, ids, targetId, suppress_events=False):
- assert targetId not in ids
-
- id2pos = self._id2pos_index
- pos2id = self._pos2id_index
- targetPos = id2pos[targetId]
- minMovedPos = min([id2pos[id] for id in ids])
- maxMovedPos = max([id2pos[id] for id in ids])
-
- for id in ids :
- assert id == pos2id.pop(id2pos.pop(id))
-
- id2posUpdate = {}
- pos2idUpdate = {}
-
- if targetPos < minMovedPos :
- # selection moved before the first item position
- for i, id in enumerate(ids) :
- pos = i + targetPos + 1
- id2posUpdate[id] = pos
- pos2idUpdate[pos] = id
-
- for id in IndexIterator(pos2id, targetPos+1, maxMovedPos):
- pos = pos + 1
- id2posUpdate[id] = pos
- pos2idUpdate[pos] = id
-
- elif targetPos > minMovedPos and targetPos < maxMovedPos :
- # selection moved between the first and last item positions
- pos = minMovedPos
- # move items placed between the first item position and the target position
- for id in IndexIterator(pos2id, minMovedPos+1, targetPos) :
- id2posUpdate[id] = pos
- pos2idUpdate[pos] = id
- pos += 1
- # move selected items
- for id in ids :
- id2posUpdate[id] = pos
- pos2idUpdate[pos] = id
- pos += 1
- # move items positioned between the target position and the moved item max position
- for id in IndexIterator(pos2id, targetPos+1, maxMovedPos) :
- id2posUpdate[id] = pos
- pos2idUpdate[pos] = id
- pos += 1
-
- else :
- # selection moved after the last item position
- pos = minMovedPos
- for id in IndexIterator(pos2id, minMovedPos+1, targetPos) :
- id2posUpdate[id] = pos
- pos2idUpdate[pos] = id
- pos += 1
-
- pos = targetPos - len(ids) + 1
- for id in ids :
- id2posUpdate[id] = pos
- pos2idUpdate[pos] = id
- pos +=1
-
- id2pos.update(id2posUpdate)
- pos2id.update(pos2idUpdate)
-
- if not suppress_events :
- for id, pos in id2posUpdate.items() :
- notify(ObjectPositionModified(self[id], self, pos))
-
- notifyContainerModified(self)
-
- def getObjectPosition(self, id):
- """ Get the position of an object by its id.
- """
- try :
- return self._id2pos_index[id]
- except KeyError :
- raise ValueError('The object with the id "%s" does not exist.' % id)
-
-
-class IndexIterator :
- def __init__(self, d, start, stop) :
- self.d = d
- self.pos = start
- self.stop = stop
-
- def __iter__(self) :
- return self
-
- def next(self) :
- try :
- if self.pos > self.stop :
- raise StopIteration
- v = self.d[self.pos]
- self.pos = self.pos + 1
- return v
- except KeyError :
- self.pos = self.pos + 1
- return self.next()
-
-
-InitializeClass(HugePlinnFolder)
-HugePlinnFolderFactory = Factory(HugePlinnFolder)
-manage_addHugePlinnFolder = HugePlinnFolder.manage_addHugePlinnFolder.im_func