1 # -*- encoding: utf-8 -*-
2 ##############################################################################
4 # Copyright (c) 2004-2008 TINY SPRL. (http://tiny.be) All Rights Reserved.
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
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 ##############################################################################
42 from widget
.screen
import Screen
44 from modules
.gui
.window
.win_search
import win_search
51 def __init__(self
, model
, id=None, attrs
=None ,domain
=None, context
=None, window
=None, view_ids
=None,target
=False):
60 window
= service
.LocalService('gui.main').window
62 self
.dia
= gtk
.Dialog(_('Tiny ERP - Link'), window
,
63 gtk
.DIALOG_MODAL|gtk
.DIALOG_DESTROY_WITH_PARENT
)
65 if ('string' in attrs
) and attrs
['string']:
66 self
.dia
.set_title(self
.dia
.get_title() + ' - ' + attrs
['string'])
68 self
.dia
.set_property('default-width', 760)
69 self
.dia
.set_property('default-height', 500)
70 self
.dia
.set_position(gtk
.WIN_POS_CENTER_ON_PARENT
)
71 self
.dia
.set_icon(common
.TINYERP_ICON
)
73 self
.accel_group
= gtk
.AccelGroup()
74 self
.dia
.add_accel_group(self
.accel_group
)
76 self
.but_cancel
= self
.dia
.add_button(gtk
.STOCK_CANCEL
, gtk
.RESPONSE_CANCEL
)
77 self
.but_cancel
.add_accelerator('clicked', self
.accel_group
, gtk
.keysyms
.Escape
, gtk
.gdk
.CONTROL_MASK
, gtk
.ACCEL_VISIBLE
)
79 self
.but_ok
= self
.dia
.add_button(gtk
.STOCK_OK
, gtk
.RESPONSE_OK
)
80 self
.but_ok
.add_accelerator('clicked', self
.accel_group
, gtk
.keysyms
.Return
, gtk
.gdk
.CONTROL_MASK
, gtk
.ACCEL_VISIBLE
)
82 scroll
= gtk
.ScrolledWindow()
83 scroll
.set_policy(gtk
.POLICY_AUTOMATIC
, gtk
.POLICY_AUTOMATIC
)
84 scroll
.set_placement(gtk
.CORNER_TOP_LEFT
)
85 scroll
.set_shadow_type(gtk
.SHADOW_NONE
)
86 self
.dia
.vbox
.pack_start(scroll
, expand
=True, fill
=True)
89 vp
.set_shadow_type(gtk
.SHADOW_NONE
)
91 self
.screen
= Screen(model
, view_ids
=view_ids
, domain
=domain
, context
=context
, window
=self
.dia
, view_type
=['form'])
93 self
.screen
.load([id])
96 vp
.add(self
.screen
.widget
)
98 x
,y
= self
.screen
.screen_container
.size_get()
99 width
, height
= window
.get_size()
100 vp
.set_size_request(min(width
- 20, x
+ 20),min(height
- 60, y
+ 25))
102 self
.screen
.display()
104 def run(self
, datas
={}):
107 if res
==gtk
.RESPONSE_OK
:
108 if self
.screen
.current_model
.validate() and self
.screen
.save_current():
109 return (True, self
.screen
.current_model
.name_get())
111 self
.screen
.display()
114 return (False, False)
117 self
.window
.present()
120 class many2one(interface
.widget_interface
):
121 def __init__(self
, window
, parent
, model
, attrs
={}):
122 interface
.widget_interface
.__init
__(self
, window
, parent
, model
, attrs
)
124 self
.widget
= gtk
.HBox(spacing
=3)
125 self
.widget
.set_property('sensitive', True)
126 self
.widget
.connect('focus-in-event', lambda x
,y
: self
._focus
_in
())
127 self
.widget
.connect('focus-out-event', lambda x
,y
: self
._focus
_out
())
129 self
.wid_text
= gtk
.Entry()
130 self
.wid_text
.set_property('width-chars', 13)
131 self
.wid_text
.connect('key_press_event', self
.sig_key_press
)
132 self
.wid_text
.connect('button_press_event', self
._menu
_open
)
133 self
.wid_text
.connect_after('changed', self
.sig_changed
)
134 self
.wid_text
.connect_after('activate', self
.sig_activate
)
135 self
.wid_text_focus_out_id
= self
.wid_text
.connect_after('focus-out-event', self
.sig_activate
, True)
136 self
.widget
.pack_start(self
.wid_text
, expand
=True, fill
=True)
138 self
.but_new
= gtk
.Button()
139 img_new
= gtk
.Image()
140 img_new
.set_from_stock('gtk-new',gtk
.ICON_SIZE_BUTTON
)
141 self
.but_new
.set_image(img_new
)
142 self
.but_new
.set_relief(gtk
.RELIEF_NONE
)
143 self
.but_new
.connect('clicked', self
.sig_new
)
144 self
.but_new
.set_alignment(0.5, 0.5)
145 self
.but_new
.set_property('can-focus', False)
146 self
.widget
.pack_start(self
.but_new
, expand
=False, fill
=False)
148 self
.but_open
= gtk
.Button()
149 img_find
= gtk
.Image()
150 img_find
.set_from_stock('gtk-find',gtk
.ICON_SIZE_BUTTON
)
151 img_open
= gtk
.Image()
152 img_open
.set_from_stock('gtk-open',gtk
.ICON_SIZE_BUTTON
)
153 self
.but_open
.set_image(img_find
)
154 self
.but_open
.set_relief(gtk
.RELIEF_NONE
)
155 self
.but_open
.connect('clicked', self
.sig_edit
)
156 self
.but_open
.set_alignment(0.5, 0.5)
157 self
.but_open
.set_property('can-focus', False)
158 self
.widget
.pack_start(self
.but_open
, padding
=2, expand
=False, fill
=False)
160 self
.tooltips
= gtk
.Tooltips()
161 self
.tooltips
.set_tip(self
.but_new
, _('Create a new resource'))
162 self
.tooltips
.set_tip(self
.but_open
, _('Open a resource'))
163 self
.tooltips
.enable()
166 self
._readonly
= False
167 self
.model_type
= attrs
['relation']
168 self
._menu
_loaded
= False
169 self
._menu
_entries
.append((None, None, None))
170 self
._menu
_entries
.append((_('Action'), lambda x
: self
.click_and_action('client_action_multi'),0))
171 self
._menu
_entries
.append((_('Report'), lambda x
: self
.click_and_action('client_print_multi'),0))
174 if attrs
.get('completion',False):
175 ids
= rpc
.session
.rpc_exec_auth('/object', 'execute', self
.attrs
['relation'], 'name_search', '', [], 'ilike', {})
177 self
.load_completion(ids
,attrs
)
179 def load_completion(self
,ids
,attrs
):
180 self
.completion
= gtk
.EntryCompletion()
181 self
.completion
.set_match_func(self
.match_func
, None)
182 self
.completion
.connect("match-selected", self
.on_completion_match
)
183 self
.wid_text
.set_completion(self
.completion
)
184 self
.liststore
= gtk
.ListStore(gobject
.TYPE_STRING
, gobject
.TYPE_STRING
)
185 self
.completion
.set_model(self
.liststore
)
186 self
.completion
.set_text_column(0)
187 for i
,word
in enumerate(ids
):
188 if word
[1][0] == '[':
189 i
= word
[1].find(']')
192 self
.liststore
.append([("%s %s" % (s
,s2
)),s2
])
194 self
.liststore
.append([word
[1],word
[1]])
196 def match_func(self
, completion
, key_string
, iter, data
):
197 model
= self
.completion
.get_model()
198 modelstr
= model
[iter][0].lower()
199 return modelstr
.startswith(key_string
)
201 def on_completion_match(self
, completion
, model
, iter):
202 name
= model
[iter][1]
203 domain
= self
._view
.modelfield
.domain_get(self
._view
.model
)
204 context
= self
._view
.modelfield
.context_get(self
._view
.model
)
205 ids
= rpc
.session
.rpc_exec_auth('/object', 'execute',
206 self
.attrs
['relation'], 'name_search', name
, domain
, 'ilike',
209 self
._view
.modelfield
.set_client(self
._view
.model
, ids
[0])
210 self
.display(self
._view
.model
, self
._view
.modelfield
)
213 win
= win_search(self
.attrs
['relation'], sel_multi
=False,
214 ids
=map(lambda x
: x
[0], ids
), context
=context
,
215 domain
=domain
, window
=self
._window
)
218 name
= rpc
.session
.rpc_exec_auth('/object', 'execute',
219 self
.attrs
['relation'], 'name_get', [ids
[0]],
220 rpc
.session
.context
)[0]
221 self
._view
.modelfield
.set_client(self
._view
.model
, name
)
226 def _readonly_set(self
, value
):
227 self
._readonly
= value
228 self
.wid_text
.set_editable(not value
)
229 self
.but_new
.set_sensitive(not value
)
231 def _color_widget(self
):
234 def _menu_sig_pref(self
, obj
):
235 self
._menu
_sig
_default
_set
()
237 def _menu_sig_default(self
, obj
):
238 res
= rpc
.session
.rpc_exec_auth('/object', 'execute', self
.attrs
['model'], 'default_get', [self
.attrs
['name']])
240 def sig_activate(self
, widget
, event
=None, leave
=False):
242 value
= self
._view
.modelfield
.get(self
._view
.model
)
244 self
.wid_text
.disconnect(self
.wid_text_focus_out_id
)
247 domain
= self
._view
.modelfield
.domain_get(self
._view
.model
)
248 context
= self
._view
.modelfield
.context_get(self
._view
.model
)
249 dia
= dialog(self
.attrs
['relation'], self
._view
.modelfield
.get(self
._view
.model
), attrs
=self
.attrs
, window
=self
._window
, domain
=domain
, context
=context
)
250 ok
, value
= dia
.run()
252 self
._view
.modelfield
.set_client(self
._view
.model
, value
,
256 if not self
._readonly
and ( self
.wid_text
.get_text() or not leave
):
257 domain
= self
._view
.modelfield
.domain_get(self
._view
.model
)
258 context
= self
._view
.modelfield
.context_get(self
._view
.model
)
259 self
.wid_text
.grab_focus()
261 ids
= rpc
.session
.rpc_exec_auth('/object', 'execute', self
.attrs
['relation'], 'name_search', self
.wid_text
.get_text(), domain
, 'ilike', context
)
263 self
._view
.modelfield
.set_client(self
._view
.model
, ids
[0],
265 self
.wid_text_focus_out_id
= self
.wid_text
.connect_after('focus-out-event', self
.sig_activate
, True)
266 self
.display(self
._view
.model
, self
._view
.modelfield
)
270 win
= win_search(self
.attrs
['relation'], sel_multi
=False, ids
=map(lambda x
: x
[0], ids
), context
=context
, domain
=domain
, parent
=self
._window
)
273 name
= rpc
.session
.rpc_exec_auth('/object', 'execute', self
.attrs
['relation'], 'name_get', [ids
[0]], rpc
.session
.context
)[0]
274 self
._view
.modelfield
.set_client(self
._view
.model
, name
,
276 self
.wid_text_focus_out_id
= self
.wid_text
.connect_after('focus-out-event', self
.sig_activate
, True)
277 self
.display(self
._view
.model
, self
._view
.modelfield
)
280 def sig_new(self
, *args
):
281 self
.wid_text
.disconnect(self
.wid_text_focus_out_id
)
282 domain
= self
._view
.modelfield
.domain_get(self
._view
.model
)
283 context
= self
._view
.modelfield
.context_get(self
._view
.model
)
284 dia
= dialog(self
.attrs
['relation'], attrs
=self
.attrs
, window
=self
._window
, domain
=domain
,context
=context
)
285 ok
, value
= dia
.run()
287 self
._view
.modelfield
.set_client(self
._view
.model
, value
)
288 self
.display(self
._view
.model
, self
._view
.modelfield
)
290 self
.wid_text_focus_out_id
= self
.wid_text
.connect_after('focus-out-event', self
.sig_activate
, True)
291 sig_edit
= sig_activate
293 def sig_key_press(self
, widget
, event
, *args
):
294 if event
.keyval
==gtk
.keysyms
.F1
:
295 self
.sig_new(widget
, event
)
296 elif event
.keyval
==gtk
.keysyms
.F2
:
297 self
.sig_activate(widget
, event
)
298 elif event
.keyval
== gtk
.keysyms
.Tab
:
299 if self
._view
.modelfield
.get(self
._view
.model
) or \
300 not self
.wid_text
.get_text():
302 self
.sig_activate(widget
, event
, leave
=True)
306 def sig_changed(self
, *args
):
308 if self
._view
.modelfield
.get(self
._view
.model
):
309 self
._view
.modelfield
.set_client(self
._view
.model
, False)
310 self
.display(self
._view
.model
, self
._view
.modelfield
)
313 def set_value(self
, model
, model_field
):
314 pass # No update of the model, the model is updated in real time !
316 def display(self
, model
, model_field
):
319 self
.wid_text
.set_text('')
321 super(many2one
, self
).display(model
, model_field
)
323 res
= model_field
.get_client(model
)
324 self
.wid_text
.set_text((res
and str(res
)) or '')
327 img
.set_from_stock('gtk-open',gtk
.ICON_SIZE_BUTTON
)
328 self
.but_open
.set_image(img
)
329 self
.tooltips
.set_tip(self
.but_open
, _('Open a resource'))
331 img
.set_from_stock('gtk-find',gtk
.ICON_SIZE_BUTTON
)
332 self
.but_open
.set_image(img
)
333 self
.tooltips
.set_tip(self
.but_open
, _('Search a resource'))
336 def _menu_open(self
, obj
, event
):
337 if event
.button
== 3:
338 value
= self
._view
.modelfield
.get(self
._view
.model
)
339 if not self
._menu
_loaded
:
340 resrelate
= rpc
.session
.rpc_exec_auth('/object', 'execute', 'ir.values', 'get', 'action', 'client_action_relate', [(self
.model_type
, False)], False, rpc
.session
.context
)
341 resrelate
= map(lambda x
:x
[2], resrelate
)
342 self
._menu
_entries
.append((None, None, None))
344 x
['string'] = x
['name']
345 f
= lambda action
: lambda x
: self
.click_and_relate(action
)
346 self
._menu
_entries
.append(('... '+x
['name'], f(x
), 0))
347 self
._menu
_loaded
= True
350 for stock_id
,callback
,sensitivity
in self
._menu
_entries
:
352 item
= gtk
.ImageMenuItem(stock_id
)
354 item
.connect("activate",callback
)
355 item
.set_sensitive(bool(sensitivity
or value
))
357 item
=gtk
.SeparatorMenuItem()
360 menu
.popup(None,None,None,event
.button
,event
.time
)
364 def click_and_relate(self
, action
):
368 id = self
._view
.modelfield
.get(self
._view
.model
)
370 common
.message(_('You must select a record to use the relation !'))
372 screen
= Screen(self
.attrs
['relation'])
374 act
['domain'] = screen
.current_model
.expr_eval(act
['domain'], check_load
=False)
375 act
['context'] = str(screen
.current_model
.expr_eval(act
['context'], check_load
=False))
376 obj
= service
.LocalService('action.main')
377 value
= obj
._exec
_action
(act
, data
, context
)
380 def click_and_action(self
, type):
381 id = self
._view
.modelfield
.get(self
._view
.model
)
382 obj
= service
.LocalService('action.main')
383 res
= obj
.exec_keyword(type, {'model':self
.model_type
, 'id': id or False, 'ids':[id], 'report_type': 'pdf'})
387 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: