bugfix
[openerp-client.git] / bin / widget / view / form_gtk / one2many_list.py
blobd0fe6989a6ee2b84a9634221a8dead0d8b638213
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 gobject
32 import gtk
34 import gettext
36 import common
37 from common import sur
38 import wid_common
40 import interface
41 from widget.screen import Screen
42 import service
45 class dialog(object):
46 def __init__(self, model_name, parent, model=None, attrs=None, model_ctx=None,
47 window=None, default_get_ctx=None, readonly=False):
49 if attrs is None:
50 attrs = {}
51 if model_ctx is None:
52 model_ctx = {}
53 if default_get_ctx is None:
54 default_get_ctx = {}
56 if not window:
57 window = service.LocalService('gui.main').window
59 self.dia = gtk.Dialog(_('OpenERP - Link'), window,
60 gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT)
61 self.window = window
62 if ('string' in attrs) and attrs['string']:
63 self.dia.set_title(self.dia.get_title() + ' - ' + attrs['string'])
64 self.dia.set_property('default-width', 760)
65 self.dia.set_property('default-height', 500)
66 self.dia.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
67 self.dia.set_icon(common.OPENERP_ICON)
69 self.accel_group = gtk.AccelGroup()
70 self.dia.add_accel_group(self.accel_group)
72 self.but_cancel = self.dia.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
73 self.but_cancel.add_accelerator('clicked', self.accel_group, gtk.keysyms.Escape, gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE)
75 self.but_ok = self.dia.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK)
76 self.but_ok.add_accelerator('clicked', self.accel_group, gtk.keysyms.Return, gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE)
78 self.default_get_ctx = default_get_ctx
80 scroll = gtk.ScrolledWindow()
81 scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
82 scroll.set_placement(gtk.CORNER_TOP_LEFT)
83 scroll.set_shadow_type(gtk.SHADOW_NONE)
84 self.dia.vbox.pack_start(scroll, expand=True, fill=True)
86 vp = gtk.Viewport()
87 vp.set_shadow_type(gtk.SHADOW_NONE)
88 scroll.add(vp)
90 self.screen = Screen(model_name, view_type=[], parent=parent, window=self.dia, readonly=readonly)
91 self.screen.models._context.update(model_ctx)
92 if not model:
93 model = self.screen.new(context=default_get_ctx)
94 else:
95 self.screen.models.model_add(model)
96 self.screen.current_model = model
97 if ('views' in attrs) and ('form' in attrs['views']):
98 arch = attrs['views']['form']['arch']
99 fields = attrs['views']['form']['fields']
100 self.screen.add_view(arch, fields, display=True,
101 context=default_get_ctx)
102 else:
103 self.screen.add_view_id(False, 'form', display=True,
104 context=default_get_ctx)
105 vp.add(self.screen.widget)
106 x,y = self.screen.screen_container.size_get()
107 vp.set_size_request(x,y+30)
108 self.dia.show_all()
109 self.screen.display()
111 def new(self):
112 model = self.screen.new(context=self.default_get_ctx)
113 self.screen.models.model_add(model)
114 self.screen.current_model = model
115 return True
117 def run(self, datas={}):
118 end = False
119 while not end:
120 res = self.dia.run()
121 end = (res != gtk.RESPONSE_OK) or self.screen.current_model.validate()
122 if not end:
123 self.screen.display()
125 if res==gtk.RESPONSE_OK:
126 self.screen.current_view.set_value()
127 model = self.screen.current_model
128 return (True, model)
129 return (False, False)
131 def destroy(self):
132 self.screen.signal_unconnect(self)
133 self.window.present()
134 self.dia.destroy()
135 self.screen.destroy()
138 class one2many_list(interface.widget_interface):
139 def __init__(self, window, parent, model, attrs={}):
140 interface.widget_interface.__init__(self, window, parent, model, attrs)
142 self._readonly = self.default_readonly
143 self.widget = gtk.VBox(homogeneous=False, spacing=5)
145 hb = gtk.HBox(homogeneous=False, spacing=5)
146 menubar = gtk.MenuBar()
147 if hasattr(menubar, 'set_pack_direction') and \
148 hasattr(menubar, 'set_child_pack_direction'):
149 menubar.set_pack_direction(gtk.PACK_DIRECTION_LTR)
150 menubar.set_child_pack_direction(gtk.PACK_DIRECTION_LTR)
152 menuitem_title = gtk.ImageMenuItem(stock_id='gtk-preferences')
154 menu_title = gtk.Menu()
155 menuitem_set_to_default = gtk.MenuItem(_('Set to default value'), True)
156 menuitem_set_to_default.connect('activate', lambda *x:self._menu_sig_default_get())
157 menu_title.add(menuitem_set_to_default)
158 menuitem_set_default = gtk.MenuItem(_('Set Default'), True)
159 menuitem_set_default.connect('activate', lambda *x: self._menu_sig_default_set())
160 menu_title.add(menuitem_set_default)
161 menuitem_title.set_submenu(menu_title)
163 menubar.add(menuitem_title)
164 hb.pack_start(menubar, expand=True, fill=True)
166 tooltips = gtk.Tooltips()
168 self.eb_new = gtk.EventBox()
169 tooltips.set_tip(self.eb_new, _('Create a new entry'))
170 self.eb_new.set_events(gtk.gdk.BUTTON_PRESS)
171 self.eb_new.connect('button_press_event', self._sig_new)
172 img_new = gtk.Image()
173 img_new.set_from_stock('gtk-new', gtk.ICON_SIZE_MENU)
174 img_new.set_alignment(0.5, 0.5)
175 self.eb_new.add(img_new)
176 hb.pack_start(self.eb_new, expand=False, fill=False)
178 self.eb_open = gtk.EventBox()
179 tooltips.set_tip(self.eb_open, _('Edit this entry'))
180 self.eb_open.set_events(gtk.gdk.BUTTON_PRESS)
181 self.eb_open.connect('button_press_event', self._sig_edit)
182 img_open = gtk.Image()
183 img_open.set_from_stock('gtk-open', gtk.ICON_SIZE_MENU)
184 img_open.set_alignment(0.5, 0.5)
185 self.eb_open.add(img_open)
186 hb.pack_start(self.eb_open, expand=False, fill=False)
188 self.eb_del = gtk.EventBox()
189 tooltips.set_tip(self.eb_del, _('Remove this entry'))
190 self.eb_del.set_events(gtk.gdk.BUTTON_PRESS)
191 self.eb_del.connect('button_press_event', self._sig_remove)
192 img_del = gtk.Image()
193 img_del.set_from_stock('gtk-delete', gtk.ICON_SIZE_MENU)
194 img_del.set_alignment(0.5, 0.5)
195 self.eb_del.add(img_del)
196 hb.pack_start(self.eb_del, expand=False, fill=False)
198 hb.pack_start(gtk.VSeparator(), expand=False, fill=True)
200 eb_pre = gtk.EventBox()
201 tooltips.set_tip(eb_pre, _('Previous'))
202 eb_pre.set_events(gtk.gdk.BUTTON_PRESS)
203 eb_pre.connect('button_press_event', self._sig_previous)
204 img_pre = gtk.Image()
205 img_pre.set_from_stock('gtk-go-back', gtk.ICON_SIZE_MENU)
206 img_pre.set_alignment(0.5, 0.5)
207 eb_pre.add(img_pre)
208 hb.pack_start(eb_pre, expand=False, fill=False)
210 self.label = gtk.Label('(0,0)')
211 hb.pack_start(self.label, expand=False, fill=False)
213 eb_next = gtk.EventBox()
214 tooltips.set_tip(eb_next, _('Next'))
215 eb_next.set_events(gtk.gdk.BUTTON_PRESS)
216 eb_next.connect('button_press_event', self._sig_next)
217 img_next = gtk.Image()
218 img_next.set_from_stock('gtk-go-forward', gtk.ICON_SIZE_MENU)
219 img_next.set_alignment(0.5, 0.5)
220 eb_next.add(img_next)
221 hb.pack_start(eb_next, expand=False, fill=False)
223 hb.pack_start(gtk.VSeparator(), expand=False, fill=True)
225 eb_switch = gtk.EventBox()
226 tooltips.set_tip(eb_switch, _('Switch'))
227 eb_switch.set_events(gtk.gdk.BUTTON_PRESS)
228 eb_switch.connect('button_press_event', self.switch_view)
229 img_switch = gtk.Image()
230 img_switch.set_from_stock('gtk-justify-left', gtk.ICON_SIZE_MENU)
231 img_switch.set_alignment(0.5, 0.5)
232 eb_switch.add(img_switch)
233 hb.pack_start(eb_switch, expand=False, fill=False)
235 tooltips.enable()
236 self.widget.pack_start(hb, expand=False, fill=True)
238 self.screen = Screen(attrs['relation'], view_type=attrs.get('mode','tree,form').split(','), parent=self.parent, views_preload=attrs.get('views', {}), tree_saves=attrs.get('saves', False), create_new=True, row_activate=self._on_activate, default_get=attrs.get('default_get', {}), window=self._window, readonly=self._readonly)
239 self.screen.signal_connect(self, 'record-message', self._sig_label)
241 menuitem_title.get_child().set_markup('<b>'+self.screen.current_view.title.replace('&','&amp;').replace('<','&lt;').replace('>','&gt;')+'</b>')
243 self.widget.pack_start(self.screen.widget, expand=True, fill=True)
245 self.screen.widget.connect('key_press_event', self.on_keypress)
247 def on_keypress(self, widget, event):
248 if (not self._readonly) and ((event.keyval == gtk.keysyms.N and event.state & gtk.gdk.CONTROL_MASK\
249 and event.state & gtk.gdk.SHIFT_MASK) or (event.keyval == gtk.keysyms.F1)):
250 self._sig_new(widget, event)
251 return False
252 if event.keyval == gtk.keysyms.F2:
253 self._sig_edit(widget, event)
254 return False
256 def destroy(self):
257 self.screen.destroy()
259 def _on_activate(self, screen, *args):
260 self._sig_edit()
262 def click_and_action(self, type):
263 pos = self.tree_view.pos_get()
264 if pos!=None:
265 val = self._value[pos]
266 id = val.get('id', False)
267 obj = service.LocalService('action.main')
268 res = obj.exec_keyword(type, {'model':self.model, 'id': id or False, 'ids':[id], 'report_type': 'pdf'})
269 return True
270 else:
271 common.message(_('You have to select a resource !'))
272 return False
274 def switch_view(self, btn, arg):
275 self.screen.switch_view()
277 def _readonly_set(self, value):
278 self._readonly = value
279 self.eb_new.set_sensitive(not value)
280 self.eb_del.set_sensitive(not value)
281 self.screen.readonly = value
282 self.screen.display()
284 def _sig_new(self, *args):
285 _, event = args
286 ctx = self._view.model.expr_eval(self.screen.default_get)
287 ctx.update(self._view.model.expr_eval('dict(%s)' % self.attrs.get('context', '')))
288 if event.type in (gtk.gdk.BUTTON_PRESS, gtk.gdk.KEY_PRESS):
289 if (self.screen.current_view.view_type=='form') or self.screen.editable_get():
290 self.screen.new(context=ctx)
291 self._readonly = False
292 self.screen.current_view.widget.set_sensitive(True)
293 else:
294 ok = 1
295 dia = dialog(self.attrs['relation'], parent=self._view.model, attrs=self.attrs, model_ctx=self.screen.models._context, default_get_ctx=ctx, window=self._window, readonly=self._readonly)
296 while ok:
297 ok, value = dia.run()
298 if ok:
299 self.screen.models.model_add(value)
300 value.signal('record-changed', value.parent)
301 self.screen.display()
302 dia.new()
303 dia.destroy()
305 def _sig_edit(self, *args):
306 if self.screen.current_model:
307 dia = dialog(self.attrs['relation'], parent=self._view.model, model=self.screen.current_model, attrs=self.attrs, window=self._window, readonly=self._readonly)
308 ok, value = dia.run()
309 dia.destroy()
311 def _sig_next(self, *args):
312 _, event = args
313 if event.type == gtk.gdk.BUTTON_PRESS:
314 self.screen.display_next()
316 def _sig_previous(self, *args):
317 _, event = args
318 if event.type == gtk.gdk.BUTTON_PRESS:
319 self.screen.display_prev()
321 def _sig_remove(self, *args):
322 _, event = args
323 if event.type == gtk.gdk.BUTTON_PRESS:
324 if self.screen.current_view.view_type == 'form':
325 msg = 'Are you sure to remove this record ?'
326 else:
327 msg = 'Are you sure to remove these records ?'
328 if common.sur(msg):
329 self.screen.remove()
330 if not self.screen.models.models:
331 self.screen.current_view.widget.set_sensitive(False)
333 def _sig_label(self, screen, signal_data):
334 name = '_'
335 if signal_data[0] >= 0:
336 name = str(signal_data[0] + 1)
337 line = '(%s/%s)' % (name, signal_data[1])
338 self.label.set_text(line)
340 def _sig_refresh(self, *args):
341 pass
343 def _sig_sequence(self, *args):
344 pass
346 def display(self, model, model_field):
347 if not model_field:
348 self.screen.current_model = None
349 self.screen.display()
350 return False
351 super(one2many_list, self).display(model, model_field)
352 new_models = model_field.get_client(model)
353 if self.screen.models != new_models:
354 self.screen.models_set(new_models)
355 if (self.screen.current_view.view_type=='tree') and self.screen.editable_get():
356 self.screen.current_model = None
357 self.screen.display()
358 else:
359 self.screen.display()
360 return True
362 def set_value(self, model, model_field):
363 self.screen.current_view.set_value()
364 if self.screen.is_modified():
365 model.modified = True
366 model.modified_fields.setdefault(model_field.name)
367 return True
369 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: