1 # -*- coding: utf-8 -*-
2 #######################################################################################
3 # Plinn - http://plinn.org #
4 # Copyright (C) 2005-2007 Benoît PIN <benoit.pin@ensmp.fr> #
6 # This program is free software; you can redistribute it and/or #
7 # modify it under the terms of the GNU General Public License #
8 # as published by the Free Software Foundation; either version 2 #
9 # of the License, or (at your option) any later version. #
11 # This program is distributed in the hope that it will be useful, #
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14 # GNU General Public License for more details. #
16 # You should have received a copy of the GNU General Public License #
17 # along with this program; if not, write to the Free Software #
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #
19 #######################################################################################
20 """ Plinn implementation of CMFBTree
27 from Products
.BTreeFolder2
.BTreeFolder2
import BTreeFolder2Base
28 from Products
.ZCatalog
.Lazy
import LazyMap
29 from BTrees
.IOBTree
import IOBTree
30 from BTrees
.OIBTree
import OIBTree
31 from Folder
import PlinnFolder
32 from zope
.event
import notify
34 from zope
.app
.container
.contained
import notifyContainerModified
37 from zope
.container
.contained
import notifyContainerModified
38 from events
import ObjectPositionModified
39 from zope
.component
.factory
import Factory
40 from Products
.CMFCore
.permissions
import AddPortalFolders
, \
42 AccessContentsInformation
43 from AccessControl
import ClassSecurityInfo
44 from Globals
import InitializeClass
45 from types
import StringType
48 class HugePlinnFolder(BTreeFolder2Base
, PlinnFolder
) :
49 """ Plinn Folder for large set of objects
52 security
= ClassSecurityInfo()
54 __getitem__
= PlinnFolder
.__getitem
__
56 def __init__(self
, id, title
='') :
57 PlinnFolder
.__init
__(self
, id, title
)
58 BTreeFolder2Base
.__init
__(self
, id)
60 def _initBTrees(self
):
61 super(HugePlinnFolder
, self
)._initBTrees
()
62 self
._pos
2id
_index
= IOBTree()
63 self
._id
2pos
_index
= OIBTree()
65 def _checkId(self
, id, allow_dup
=0) :
66 PlinnFolder
._checkId
(self
, id, allow_dup
)
67 BTreeFolder2Base
._checkId
(self
, id, allow_dup
)
69 security
.declareProtected(AddPortalFolders
, 'manage_addHugePlinnFolder')
70 def manage_addHugePlinnFolder(self
, id, title
='', REQUEST
=None) :
71 """ Add new a new HugePlinnFolder object with id *id*.
73 ob
= HugePlinnFolder(id, title
)
74 self
._setObject
(id, ob
)
75 if REQUEST
is not None :
76 return self
.folder_contents(self
, REQUEST
, portal_status_message
='Folder added')
78 def _setOb(self
, id, object):
79 super(HugePlinnFolder
, self
)._setOb
(id, object)
80 pos
= self
.objectCount() - 1
81 self
._pos
2id
_index
[pos
] = id
82 self
._id
2pos
_index
[id] = pos
85 pos
= self
._id
2pos
_index
[id]
86 self
._id
2pos
_index
.pop(id)
88 for p
in xrange(pos
+1, self
.objectCount()) :
89 ident
= self
._pos
2id
_index
[p
]
90 self
._pos
2id
_index
[p
-1] = ident
91 self
._id
2pos
_index
[ident
] = p
-1
93 self
._pos
2id
_index
.pop(self
.objectCount()-1)
95 super(HugePlinnFolder
, self
)._delOb
(id)
97 security
.declareProtected(AccessContentsInformation
, 'objectIds')
98 def objectIds(self
, spec
=None) :
100 return super(HugePlinnFolder
, self
).objectIds(spec
)
102 pos2id
= lambda pos
: self
._pos
2id
_index
[pos
]
103 return LazyMap(pos2id
, xrange(self
.objectCount()))
107 security
.declareProtected(ManageProperties
, 'moveObjectsByDelta')
108 def moveObjectsByDelta(self
, ids
, delta
, subset_ids
=None,
109 suppress_events
=False):
110 """ Move specified sub-objects by delta.
112 if isinstance(ids
, StringType
):
115 id2pos
= self
._id
2pos
_index
116 pos2id
= self
._pos
2id
_index
118 oldPosition
= id2pos
[id]
119 newPosition
= max(oldPosition
+ delta
, 0)
121 shift
= delta
> 0 and 1 or -1
122 for p
in xrange(oldPosition
, newPosition
, shift
) :
123 ident
= pos2id
[p
+shift
]
126 if not suppress_events
:
127 notify(ObjectPositionModified(self
[ident
], self
, p
))
129 id2pos
[id] = newPosition
130 pos2id
[newPosition
] = id
131 if not suppress_events
:
132 notify(ObjectPositionModified(self
[id], self
, newPosition
))
134 if not suppress_events
:
135 notifyContainerModified(self
)
137 security
.declareProtected(ManageProperties
, 'moveObjectsAfter')
138 def moveObjectsAfter(self
, ids
, targetId
, suppress_events
=False):
139 assert targetId
not in ids
141 id2pos
= self
._id
2pos
_index
142 pos2id
= self
._pos
2id
_index
143 targetPos
= id2pos
[targetId
]
144 minMovedPos
= min([id2pos
[id] for id in ids
])
145 maxMovedPos
= max([id2pos
[id] for id in ids
])
148 assert id == pos2id
.pop(id2pos
.pop(id))
153 if targetPos
< minMovedPos
:
154 # selection moved before the first item position
155 for i
, id in enumerate(ids
) :
156 pos
= i
+ targetPos
+ 1
157 id2posUpdate
[id] = pos
158 pos2idUpdate
[pos
] = id
160 for id in IndexIterator(pos2id
, targetPos
+1, maxMovedPos
):
162 id2posUpdate
[id] = pos
163 pos2idUpdate
[pos
] = id
165 elif targetPos
> minMovedPos
and targetPos
< maxMovedPos
:
166 # selection moved between the first and last item positions
168 # move items placed between the first item position and the target position
169 for id in IndexIterator(pos2id
, minMovedPos
+1, targetPos
) :
170 id2posUpdate
[id] = pos
171 pos2idUpdate
[pos
] = id
173 # move selected items
175 id2posUpdate
[id] = pos
176 pos2idUpdate
[pos
] = id
178 # move items positioned between the target position and the moved item max position
179 for id in IndexIterator(pos2id
, targetPos
+1, maxMovedPos
) :
180 id2posUpdate
[id] = pos
181 pos2idUpdate
[pos
] = id
185 # selection moved after the last item position
187 for id in IndexIterator(pos2id
, minMovedPos
+1, targetPos
) :
188 id2posUpdate
[id] = pos
189 pos2idUpdate
[pos
] = id
192 pos
= targetPos
- len(ids
) + 1
194 id2posUpdate
[id] = pos
195 pos2idUpdate
[pos
] = id
198 id2pos
.update(id2posUpdate
)
199 pos2id
.update(pos2idUpdate
)
201 if not suppress_events
:
202 for id, pos
in id2posUpdate
.items() :
203 notify(ObjectPositionModified(self
[id], self
, pos
))
205 notifyContainerModified(self
)
207 def getObjectPosition(self
, id):
208 """ Get the position of an object by its id.
211 return self
._id
2pos
_index
[id]
213 raise ValueError('The object with the id "%s" does not exist.' % id)
216 class IndexIterator
:
217 def __init__(self
, d
, start
, stop
) :
227 if self
.pos
> self
.stop
:
230 self
.pos
= self
.pos
+ 1
233 self
.pos
= self
.pos
+ 1
237 InitializeClass(HugePlinnFolder
)
238 HugePlinnFolderFactory
= Factory(HugePlinnFolder
)
239 manage_addHugePlinnFolder
= HugePlinnFolder
.manage_addHugePlinnFolder
.im_func