Merge
[openerp-client.git] / bin / widget / model / group.py
blob6b754aa348b4f608014ba0f0de256f49601c7dfe
1 # -*- encoding: utf-8 -*-
2 ##############################################################################
4 # Copyright (c) 2004-2008 TINY SPRL. (http://tiny.be) All Rights Reserved.
6 # $Id$
8 # WARNING: This program as such is intended to be used by professional
9 # programmers who take the whole responsability of assessing all potential
10 # consequences resulting from its eventual inadequacies and bugs
11 # End users who are looking for a ready-to-use solution with commercial
12 # garantees and support are strongly adviced to contract a Free Software
13 # Service Company
15 # This program is Free Software; you can redistribute it and/or
16 # modify it under the terms of the GNU General Public License
17 # as published by the Free Software Foundation; either version 2
18 # of the License, or (at your option) any later version.
20 # This program is distributed in the hope that it will be useful,
21 # but WITHOUT ANY WARRANTY; without even the implied warranty of
22 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 # GNU General Public License for more details.
25 # You should have received a copy of the GNU General Public License
26 # along with this program; if not, write to the Free Software
27 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 ##############################################################################
31 from rpc import RPCProxy
32 import rpc
33 from record import ModelRecord
34 import field
36 import signal_event
38 try:
39 a = set()
40 except NameError:
41 from sets import Set as set
43 class ModelList(list):
44 def __init__(self, screen):
45 super(ModelList, self).__init__()
46 self.lock_signal = False
47 self.__screen = screen
49 def insert(self, pos, obj):
50 super(ModelList, self).insert(pos, obj)
51 if not self.lock_signal:
52 self.__screen.signal('record-changed', ('record-added', pos))
54 def append(self, obj):
55 super(ModelList, self).append(obj)
56 if not self.lock_signal:
57 self.__screen.signal('record-changed', ('record-added', -1))
59 def move(self, obj, pos):
60 self.lock_signal = True
61 if self.__len__() > pos:
62 idx = self.index(obj)
63 self.remove(obj)
64 if pos > idx:
65 pos -= 1
66 self.insert(pos, obj)
67 else:
68 self.remove(obj)
69 self.append(obj)
70 self.lock_signal = False
72 def remove(self, obj):
73 idx = self.index(obj)
74 super(ModelList, self).remove(obj)
75 if not self.lock_signal:
76 self.__screen.signal('record-changed', ('record-removed', idx))
78 def clear(self):
79 for obj in range(len(self)):
80 self.pop()
81 if not self.lock_signal:
82 self.__screen.signal('record-changed', ('record-removed', len(self)))
84 def __setitem__(self, key, value):
85 super(ModelList, self).__setitem__(key, value)
86 if not self.lock_signal:
87 self.__screen.signal('record-changed', ('record-changed', key))
89 class ModelRecordGroup(signal_event.signal_event):
90 def __init__(self, resource, fields, ids=[], parent=None, context={}):
91 super(ModelRecordGroup, self).__init__()
92 self.parent = parent
93 self._context = context
94 self._context.update(rpc.session.context)
95 self.resource = resource
96 self.rpc = RPCProxy(resource)
97 self.fields = fields
98 self.mfields = {}
99 self.mfields_load(fields.keys(), self)
101 self.models = ModelList(self)
102 self.current_idx = None
104 self.load(ids)
105 self.model_removed = []
106 self.on_write = ''
108 def mfields_load(self, fkeys, models):
109 for fname in fkeys:
110 fvalue = models.fields[fname]
111 modelfield = field.ModelField(fvalue['type'])
112 fvalue['name'] = fname
113 models.mfields[fname] = modelfield(models, fvalue)
115 def model_move(self, model, position=0):
116 self.models.move(model, position)
118 def set_sequence(self, field='sequence'):
119 index = 0
120 for model in self.models:
121 if model[field]:
122 if index >= model[field].get(model):
123 index += 1
124 model[field].set(model, index, modified=True)
125 else:
126 index = model[field].get(model)
127 if model.id:
128 model.save()
130 def save(self):
131 for model in self.models:
132 saved = model.save()
133 self.writen(saved)
135 def writen(self, edited_id):
136 if not self.on_write:
137 return
138 new_ids = getattr(self.rpc, self.on_write)(edited_id, self.context)
139 model_idx = self.models.index(self[edited_id])
140 result = False
141 for id in new_ids:
142 cont = False
143 for m in self.models:
144 if m.id == id:
145 cont = True
146 m.reload()
147 if cont:
148 continue
149 newmod = ModelRecord(self.resource, id, parent=self.parent, group=self)
150 newmod.reload()
151 if not result:
152 result = newmod
153 new_index = min(model_idx, len(self.models)-1)
154 self.model_add(newmod, new_index)
155 return result
157 def pre_load(self, ids, display=True):
158 if not ids:
159 return True
160 if len(ids)>10:
161 self.models.lock_signal = True
162 for id in ids:
163 newmod = ModelRecord(self.resource, id, parent=self.parent, group=self)
164 self.model_add(newmod)
165 if display:
166 self.signal('model-changed', newmod)
167 if len(ids)>10:
168 self.models.lock_signal = False
169 self.signal('record-cleared')
170 return True
172 def load_for(self, values):
173 if len(values)>10:
174 self.models.lock_signal = True
175 for value in values:
176 newmod = ModelRecord(self.resource, value['id'], parent=self.parent, group=self)
177 newmod.set(value)
178 self.models.append(newmod)
179 newmod.signal_connect(self, 'record-changed', self._record_changed)
180 if len(values)>10:
181 self.models.lock_signal = False
182 self.signal('record-cleared')
184 def load(self, ids, display=True):
185 if not ids:
186 return True
187 if not self.fields:
188 return self.pre_load(ids, display)
189 c = rpc.session.context.copy()
190 c.update(self.context)
191 values = self.rpc.read(ids, self.fields.keys(), c)
192 if not values:
193 return False
194 newmod = False
195 self.load_for(values)
196 if newmod and display:
197 self.signal('model-changed', newmod)
198 self.current_idx = 0
199 return True
201 def clear(self):
202 self.models.clear()
203 self.model_removed = []
205 def getContext(self):
206 ctx = {}
207 ctx.update(self._context)
208 #ctx['active_ids'] = [model.id for model in self.models if model.id]
209 #if self.current_idx is not None:
210 # ctx['active_id'] = self.models[self.current_idx].id or False
211 #else:
212 # ctx['active_id'] = False
213 return ctx
214 context = property(getContext)
216 def model_add(self, model, position=-1):
218 # To be checked
220 if not model.mgroup is self:
221 fields = {}
222 for mf in model.mgroup.fields:
223 fields[model.mgroup.fields[mf]['name']] = model.mgroup.fields[mf]
224 self.add_fields(fields, self)
225 self.add_fields(self.fields, model.mgroup)
226 model.mgroup = self
228 if position==-1:
229 self.models.append(model)
230 else:
231 self.models.insert(position, model)
232 self.current_idx = position
233 model.parent = self.parent
234 model.signal_connect(self, 'record-changed', self._record_changed)
235 return model
237 def model_new(self, default=True, domain=[], context={}):
238 newmod = ModelRecord(self.resource, None, group=self,
239 parent=self.parent, new=True)
240 newmod.signal_connect(self, 'record-changed', self._record_changed)
241 if default:
242 ctx=context.copy()
243 ctx.update(self.context)
244 newmod.default_get(domain, ctx)
245 self.signal('model-changed', newmod)
246 return newmod
248 def model_remove(self, model):
249 idx = self.models.index(model)
250 self.models.remove(model)
251 if model.parent:
252 model.parent.modified = True
253 if self.models:
254 self.current_idx = min(idx, len(self.models)-1)
255 else:
256 self.current_idx = None
258 def _record_changed(self, model, signal_data):
259 self.signal('model-changed', model)
261 def prev(self):
262 if self.models and self.current_idx is not None:
263 self.current_idx = (self.current_idx - 1) % len(self.models)
264 elif self.models:
265 self.current_idx = 0
266 else:
267 return None
268 return self.models[self.current_idx]
270 def next(self):
271 if self.models and self.current_idx is not None:
272 self.current_idx = (self.current_idx + 1) % len(self.models)
273 elif self.models:
274 self.current_idx = 0
275 else:
276 return None
277 return self.models[self.current_idx]
279 def remove(self, model):
280 try:
281 idx = self.models.index(model)
282 if self.models[idx].id:
283 self.model_removed.append(self.models[idx].id)
284 if model.parent:
285 model.parent.modified = True
286 self.models.remove(self.models[idx])
287 except:
288 pass
290 def add_fields_custom(self, fields, models):
291 to_add = []
292 for f in fields.keys():
293 if not f in models.fields:
294 models.fields[f] = fields[f]
295 models.fields[f]['name'] = f
296 to_add.append(f)
297 else:
298 models.fields[f].update(fields[f])
299 self.mfields_load(to_add, models)
300 for fname in to_add:
301 for m in models.models:
302 m.value[fname] = self.mfields[fname].create(m)
303 return to_add
305 def add_fields(self, fields, models, context=None):
306 import time
307 ct = time.time()
308 if context is None:
309 context = {}
310 to_add = self.add_fields_custom(fields, models)
311 models = models.models
312 if not len(models):
313 return True
315 old = []
316 new = []
317 for model in models:
318 if model.id:
319 old.append(model.id)
320 else:
321 new.append(model)
322 ctx = context.copy()
323 if len(old) and len(to_add):
324 ctx.update(rpc.session.context)
325 ctx.update(self.context)
327 to_add_normal = []
328 to_add_binary = []
329 for f in to_add:
330 if fields[f]['type'] in ('image','binary'):
331 to_add_binary.append(f)
332 else:
333 to_add_normal.append(f)
334 if to_add_normal:
335 values = self.rpc.read(old, to_add_normal, ctx)
336 if values:
337 for v in values:
338 id = v['id']
339 if 'id' not in to_add:
340 del v['id']
341 self[id].set(v, signal=False)
342 if to_add_binary:
343 data = {}
344 for b in to_add_binary:
345 data[b] = None
346 for m in self.models:
347 m.set(data)
349 # values = self.rpc.read(old, to_add, ctx)
350 # if values:
351 # for v in values:
352 # id = v['id']
353 # if 'id' not in to_add:
354 # del v['id']
355 # self[id].set(v, signal=False)
356 if len(new) and len(to_add):
357 ctx.update(self.context)
358 values = self.rpc.default_get(to_add, ctx)
359 for t in to_add:
360 if t not in values:
361 values[t] = False
362 for mod in new:
363 mod.set_default(values)
365 def __iter__(self):
366 return iter(self.models)
368 def get_by_id(self, id):
369 for model in self.models:
370 if model.id == id:
371 return model
373 __getitem__ = get_by_id
376 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: