bugfix with calendar views
[openerp-client.git] / bin / modules / gui / window / form.py
blob8a3c00e633d4860dc9889c37061e20b3683c25ae
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 import types
32 import gettext
34 import gtk
35 import gobject
36 from gtk import glade
38 import rpc
39 import win_selection
40 import win_search
41 import win_export
42 import win_import
43 import win_list
45 from gtk.gdk import Color
47 import common
48 import service
49 import options
50 import copy
52 import gc
54 from observator import oregistry
55 from widget.screen import Screen
58 class form(object):
59 def __init__(self, model, res_id=False, domain=None, view_type=None,
60 view_ids=None, window=None, context=None, name=False, limit=80,
61 auto_refresh=False):
62 if not view_type:
63 view_type = ['form','tree']
64 if domain is None:
65 domain = []
66 if view_ids is None:
67 view_ids = []
68 if context is None:
69 context = {}
71 fields = {}
72 self.model = model
73 self.window = window
74 self.previous_action = None
75 self.glade = glade.XML(common.terp_path("openerp.glade"),'win_form_container',gettext.textdomain())
76 self.widget = self.glade.get_widget('win_form_container')
77 self.widget.show_all()
78 self.fields = fields
79 self.domain = domain
80 self.context = context
82 self.screen = Screen(self.model, view_type=view_type,
83 context=self.context, view_ids=view_ids, domain=domain,
84 hastoolbar=options.options['form.toolbar'], show_search=True,
85 window=self.window, limit=limit, readonly=bool(auto_refresh))
86 self.screen.signal_connect(self, 'record-message', self._record_message)
87 self.screen.widget.show()
88 oregistry.add_receiver('misc-message', self._misc_message)
90 if not name:
91 self.name = self.screen.current_view.title
92 else:
93 self.name = name
94 vp = gtk.Viewport()
95 vp.set_shadow_type(gtk.SHADOW_NONE)
96 vp.add(self.screen.widget)
97 vp.show()
98 self.sw = gtk.ScrolledWindow()
99 self.sw.set_shadow_type(gtk.SHADOW_NONE)
100 self.sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
101 self.sw.add(vp)
102 self.sw.show()
104 self.has_backup = False
105 self.backup = {}
107 self.widget.pack_start(self.sw)
108 self.handlers = {
109 'but_new': self.sig_new,
110 'but_copy': self.sig_copy,
111 'but_save': self.sig_save,
112 'but_save_as': self.sig_save_as,
113 'but_import': self.sig_import,
114 'but_print_repeat': self.sig_print_repeat,
115 'but_remove': self.sig_remove,
116 'but_search': self.sig_search,
117 'but_previous': self.sig_previous,
118 'but_next': self.sig_next,
119 'but_goto_id': self.sig_goto,
120 'but_log': self.sig_logs,
121 'but_print': self.sig_print,
122 'but_reload': self.sig_reload,
123 'but_print_html': self.sig_print_html,
124 'but_action': self.sig_action,
125 'but_switch': self.sig_switch,
126 'but_attach': self.sig_attach,
127 'but_close': self.sig_close,
129 if 'tree' in view_type:
130 self.handlers['radio_tree'] = self.sig_switch_tree
131 if 'form' in view_type:
132 self.handlers['radio_form'] = self.sig_switch_form
133 if 'graph' in view_type:
134 self.handlers['radio_graph'] = self.sig_switch_graph
135 if 'calendar' in view_type:
136 self.handlers['radio_calendar'] = self.sig_switch_calendar
137 if res_id:
138 if isinstance(res_id, int):
139 res_id = [res_id]
140 self.screen.load(res_id)
141 else:
142 if self.screen.current_view.view_type == 'form':
143 self.sig_new(autosave=False)
144 if self.screen.current_view.view_type in ('tree', 'graph'):
145 self.screen.search_filter()
147 if auto_refresh and int(auto_refresh):
148 gobject.timeout_add(int(auto_refresh) * 1000, self.sig_reload)
150 def sig_switch_form(self, widget=None):
151 return self.sig_switch(widget, 'form')
153 def sig_switch_tree(self, widget=None):
154 return self.sig_switch(widget, 'tree')
156 def sig_switch_calendar(self, widget=None):
157 return self.sig_switch(widget, 'calendar')
159 def sig_switch_graph(self, widget=None):
160 return self.sig_switch(widget, 'graph')
162 def sig_goto(self, *args):
163 if not self.modified_save():
164 return
165 glade2 = glade.XML(common.terp_path("openerp.glade"),'dia_goto_id',gettext.textdomain())
166 widget = glade2.get_widget('goto_spinbutton')
167 win = glade2.get_widget('dia_goto_id')
168 win.set_transient_for(self.window)
169 win.show_all()
171 response = win.run()
172 win.destroy()
173 if response == gtk.RESPONSE_OK:
174 self.screen.load( [int(widget.get_value())] )
176 def destroy(self):
177 oregistry.remove_receiver('misc-message', self._misc_message)
178 self.screen.signal_unconnect(self)
179 self.screen.destroy()
180 del self.screen
181 del self.glade
182 del self.widget
183 self.sw.destroy()
184 del self.sw
185 gc.collect()
187 def ids_get(self):
188 return self.screen.ids_get()
190 def id_get(self):
191 return self.screen.id_get()
193 def sig_attach(self, widget=None):
194 id = self.screen.id_get()
195 if id:
196 import win_attach
197 win = win_attach.win_attach(self.model, id, parent=self.window)
198 win.go()
199 else:
200 self.message_state(_('No record selected ! You can only attach to existing record.'), color='red')
201 return True
203 def sig_switch(self, widget=None, mode=None):
204 if not self.modified_save():
205 return
206 if mode<>self.screen.current_view.view_type:
207 self.screen.switch_view(mode=mode)
209 def _id_get(self):
210 return self.screen.id_get()
212 def sig_logs(self, widget=None):
213 id = self._id_get()
214 if not id:
215 self.message_state(_('You have to select a record !'), color='red')
216 return False
217 res = rpc.session.rpc_exec_auth('/object', 'execute', self.model, 'perm_read', [id])
218 message = ''
219 for line in res:
220 todo = [
221 ('id', _('ID')),
222 ('create_uid', _('Creation User')),
223 ('create_date', _('Creation Date')),
224 ('write_uid', _('Latest Modification by')),
225 ('write_date', _('Latest Modification Date'))
227 for (key,val) in todo:
228 if line[key] and key in ('create_uid','write_uid','uid'):
229 line[key] = line[key][1]
230 message+=val+': '+str(line[key] or '/')+'\n'
231 common.message(message)
232 return True
234 def sig_remove(self, widget=None):
235 if not self._id_get():
236 msg = _('Record is not saved ! \n Do You want to Clear Current Record ?')
237 else:
238 if self.screen.current_view.view_type == 'form':
239 msg = _('Are you sure to remove this record ?')
240 else:
241 msg = _('Are you sure to remove those records ?')
242 if common.sur(msg):
243 id = self.screen.remove(unlink=True)
244 if not id:
245 self.message_state(_('Resources cleared.'), color='darkgreen')
246 else:
247 self.message_state(_('Resources successfully removed.'), color='darkgreen')
249 def sig_import(self, widget=None):
250 fields = []
251 while(self.screen.view_to_load):
252 self.screen.load_view_to_load()
253 win = win_import.win_import(self.model, self.screen.fields, fields, parent=self.window)
254 res = win.go()
256 def sig_save_as(self, widget=None):
257 fields = []
258 while(self.screen.view_to_load):
259 self.screen.load_view_to_load()
260 win = win_export.win_export(self.model, self.screen.ids_get(), self.screen.fields, fields, parent=self.window, context=self.context)
261 res = win.go()
263 def sig_new(self, widget=None, autosave=True):
264 if autosave:
265 if not self.modified_save():
266 return
267 self.screen.new()
268 self.message_state('')
270 def sig_copy(self, *args):
271 if not self.modified_save():
272 return
273 res_id = self._id_get()
274 ctx = self.context.copy()
275 ctx.update(rpc.session.context)
276 new_id = rpc.session.rpc_exec_auth('/object', 'execute', self.model, 'copy', res_id, {}, ctx)
277 if new_id:
278 self.screen.load([new_id])
279 self.message_state(_('Working now on the duplicated document !'))
281 def _form_save(self, auto_continue=True):
282 pass
284 def sig_save(self, widget=None, sig_new=True, auto_continue=True):
285 id = self.screen.save_current()
286 if id:
287 self.message_state(_('Document Saved.'), color="darkgreen")
288 else:
289 self.message_state(_('Invalid form, correct red fields !'), color="red")
290 return bool(id)
292 def sig_previous(self, widget=None):
293 if not self.modified_save():
294 return
295 self.screen.display_prev()
296 self.message_state('')
298 def sig_next(self, widget=None):
299 if not self.modified_save():
300 return
301 self.screen.display_next()
302 self.message_state('')
304 def sig_reload(self, test_modified=True):
305 if not hasattr(self, 'screen'):
306 return False
307 if test_modified and self.screen.is_modified():
308 res = common.sur_3b(_('This record has been modified\n' \
309 'do you want to save it ?'))
310 if res == 'ok':
311 self.sig_save()
312 elif res == 'ko':
313 pass
314 else:
315 return False
316 if self.screen.current_view.view_type == 'form':
317 self.screen.cancel_current()
318 self.screen.display()
319 else:
320 id = self.screen.id_get()
321 self.screen.search_filter()
322 for model in self.screen.models:
323 if model.id == id:
324 self.screen.current_model = model
325 self.screen.display()
326 break
327 self.message_state('')
328 return True
330 def sig_action(self, keyword='client_action_multi', previous=False, report_type='pdf', adds={}):
331 ids = self.screen.ids_get()
332 if self.screen.current_model:
333 id = self.screen.current_model.id
334 else:
335 id = False
336 if self.screen.current_view.view_type == 'form':
337 id = self.screen.save_current()
338 if not id:
339 return False
340 ids = [id]
341 if self.screen.current_view.view_type == 'tree':
342 sel_ids = self.screen.current_view.sel_ids_get()
343 if sel_ids:
344 ids = sel_ids
345 if len(ids):
346 obj = service.LocalService('action.main')
347 if previous and self.previous_action:
348 obj._exec_action(self.previous_action[1], {'model':self.screen.resource, 'id': id or False, 'ids':ids, 'report_type': report_type}, self.screen.context)
349 else:
350 res = obj.exec_keyword(keyword, {'model':self.screen.resource, 'id': id or False, 'ids':ids, 'report_type': report_type}, adds, self.screen.context)
351 if res:
352 self.previous_action = res
353 self.sig_reload(test_modified=False)
354 else:
355 self.message_state(_('You must select one or several records !'),color='red')
357 def sig_print_repeat(self):
358 self.sig_action('client_print_multi', True)
360 def sig_print_html(self):
361 self.sig_action('client_print_multi', report_type='html')
363 def sig_print(self):
364 self.sig_action('client_print_multi', adds={_('Print Screen'): {'report_name':'printscreen.list', 'name':_('Print Screen'), 'type':'ir.actions.report.xml'}})
366 def sig_search(self, widget=None):
367 if not self.modified_save():
368 return
369 dom = self.domain
370 win = win_search.win_search(self.model, domain=self.domain, context=self.context, parent=self.window)
371 res = win.go()
372 if res:
373 self.screen.clear()
374 self.screen.load(res)
376 def message_state(self, message, context='message', color=None):
377 sb = self.glade.get_widget('stat_state')
378 if color is not None:
379 message = '<span foreground="%s">%s</span>' % (color, message)
380 sb.set_label(message)
382 def _record_message(self, screen, signal_data):
383 if not signal_data[3]:
384 msg = _('No record selected')
385 else:
386 name = '_'
387 if signal_data[0]>=0:
388 name = str(signal_data[0]+1)
389 name2 = _('New document')
390 if signal_data[3]:
391 name2 = _('Editing document (id: ')+str(signal_data[3])+')'
392 msg = _('Record: ') + name + ' / ' + str(signal_data[1]) + \
393 _(' of ') + str(signal_data[2]) + ' - ' + name2
394 sb = self.glade.get_widget('stat_form')
395 cid = sb.get_context_id('message')
396 sb.push(cid, msg)
398 def _misc_message(self, obj, message, color=None):
399 self.message_state(message, color=color)
401 def modified_save(self, reload=True):
402 if self.screen.is_modified():
403 value = common.sur_3b(_('This record has been modified\ndo you want to save it ?'))
404 if value == 'ok':
405 return self.sig_save()
406 elif value == 'ko':
407 if reload:
408 self.sig_reload(test_modified=False)
409 return True
410 else:
411 return False
412 return True
414 def sig_close(self, urgent=False):
415 return self.modified_save(reload=False)
417 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: