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 ##############################################################################
47 from window
import win_preference
, win_extension
53 def _refresh_dblist(db_widget
, url
, dbtoload
=None):
55 dbtoload
= options
.options
['login.db']
57 liststore
= db_widget
.get_model()
59 result
= rpc
.session
.list_db(url
)
62 for db_num
, db_name
in enumerate(rpc
.session
.list_db(url
)):
63 liststore
.append([db_name
])
64 if db_name
== dbtoload
:
66 db_widget
.set_active(index
)
69 def _refresh_langlist(lang_widget
, url
):
70 liststore
= lang_widget
.get_model()
72 lang_list
= rpc
.session
.db_exec_no_except(url
, 'list_lang')
73 lang_list
.append( ('en_US','English') )
74 for key
,val
in lang_list
:
75 liststore
.insert(0, (val
,key
))
76 lang_widget
.set_active(0)
79 def _server_ask(server_widget
, parent
=None):
81 win_gl
= glade
.XML(common
.terp_path("openerp.glade"),"win_server",gettext
.textdomain())
82 win
= win_gl
.get_widget('win_server')
84 parent
= service
.LocalService('gui.main').window
85 win
.set_transient_for(parent
)
86 win
.set_icon(common
.OPENERP_ICON
)
88 win
.set_default_response(gtk
.RESPONSE_OK
)
89 host_widget
= win_gl
.get_widget('ent_host')
90 port_widget
= win_gl
.get_widget('ent_port')
91 protocol_widget
= win_gl
.get_widget('protocol')
93 protocol
={'XML-RPC': 'http://',
94 'XML-RPC secure': 'https://',
95 'NET-RPC (faster)': 'socket://',}
96 listprotocol
= gtk
.ListStore(str)
97 protocol_widget
.set_model(listprotocol
)
100 m
= re
.match('^(http[s]?://|socket://)([\w.-]+):(\d{1,5})$', server_widget
.get_text())
102 host_widget
.set_text(m
.group(2))
103 port_widget
.set_text(m
.group(3))
108 listprotocol
.append([p
])
109 if m
and protocol
[p
] == m
.group(1):
112 protocol_widget
.set_active(index
)
115 if res
== gtk
.RESPONSE_OK
:
116 protocol
= protocol
[protocol_widget
.get_active_text()]
117 url
= '%s%s:%s' % (protocol
, host_widget
.get_text(), port_widget
.get_text())
118 server_widget
.set_text(url
)
125 class db_login(object):
127 self
.win_gl
= glade
.XML(common
.terp_path("openerp.glade"),"win_login",gettext
.textdomain())
129 def refreshlist(self
, widget
, db_widget
, label
, url
, butconnect
=False):
131 def check_server_version(url
):
134 full_server_version
= rpc
.session
.db_exec_no_except(url
, 'server_version')
135 server_version
= full_server_version
.split('.')
136 client_version
= release
.version
.split('.')
137 return (server_version
[:2] == client_version
[:2], full_server_version
, release
.version
)
139 # the server doesn't understand the request. It's mean that it's an old version of the server
140 return (False, _('Unknown'), release
.version
)
142 res
= _refresh_dblist(db_widget
, url
)
144 label
.set_label('<b>'+_('Could not connect to server !')+'</b>')
148 butconnect
.set_sensitive(False)
151 label
.set_label('<b>'+_('No database found, you must create one !')+'</b>')
155 butconnect
.set_sensitive(False)
160 butconnect
.set_sensitive(True)
162 is_same_version
, server_version
, client_version
= check_server_version(url
)
163 if not is_same_version
:
164 common
.warning(_('The versions of the server (%s) and the client (%s) missmatch. The client may not work properly. Use it at your own risks.') % (server_version
, client_version
,))
167 def refreshlist_ask(self
,widget
, server_widget
, db_widget
, label
, butconnect
= False, url
=False, parent
=None):
168 url
= _server_ask(server_widget
, parent
) or url
169 return self
.refreshlist(widget
, db_widget
, label
, url
, butconnect
)
171 def run(self
, dbname
=None, parent
=None):
173 win
= self
.win_gl
.get_widget('win_login')
175 parent
= service
.LocalService('gui.main').window
176 win
.set_transient_for(parent
)
177 win
.set_icon(common
.OPENERP_ICON
)
179 img
= self
.win_gl
.get_widget('image_tinyerp')
180 img
.set_from_file(common
.terp_path_pixmaps('openerp.png'))
181 login
= self
.win_gl
.get_widget('ent_login')
182 passwd
= self
.win_gl
.get_widget('ent_passwd')
183 server_widget
= self
.win_gl
.get_widget('ent_server')
184 but_connect
= self
.win_gl
.get_widget('button_connect')
185 db_widget
= self
.win_gl
.get_widget('combo_db')
186 change_button
= self
.win_gl
.get_widget('but_server')
187 label
= self
.win_gl
.get_widget('combo_label')
190 host
= options
.options
['login.server']
191 port
= options
.options
['login.port']
192 protocol
= options
.options
['login.protocol']
194 url
= '%s%s:%s' % (protocol
, host
, port
)
195 server_widget
.set_text(url
)
196 login
.set_text(options
.options
['login.login'])
198 # construct the list of available db and select the last one used
199 liststore
= gtk
.ListStore(str)
200 db_widget
.set_model(liststore
)
201 cell
= gtk
.CellRendererText()
202 db_widget
.pack_start(cell
, True)
203 db_widget
.add_attribute(cell
, 'text', 0)
205 res
= self
.refreshlist(None, db_widget
, label
, url
, but_connect
)
206 change_button
.connect_after('clicked', self
.refreshlist_ask
, server_widget
, db_widget
, label
, but_connect
, url
, win
)
209 iter = liststore
.get_iter_root()
211 if liststore
.get_value(iter, 0)==dbname
:
212 db_widget
.set_active_iter(iter)
214 iter = liststore
.iter_next(iter)
217 m
= re
.match('^(http[s]?://|socket://)([\w.\-]+):(\d{1,5})$', server_widget
.get_text() or '')
219 options
.options
['login.server'] = m
.group(2)
220 options
.options
['login.login'] = login
.get_text()
221 options
.options
['login.port'] = m
.group(3)
222 options
.options
['login.protocol'] = m
.group(1)
223 options
.options
['login.db'] = db_widget
.get_active_text()
224 result
= (login
.get_text(), passwd
.get_text(), m
.group(2), m
.group(3), m
.group(1), db_widget
.get_active_text())
228 raise Exception('QueryCanceled')
229 if res
<> gtk
.RESPONSE_OK
:
232 raise Exception('QueryCanceled')
237 class db_create(object):
238 def set_sensitive(self
, sensitive
):
239 self
.dialog
.get_widget('button_db_ok').set_sensitive(False)
242 def server_change(self
, widget
=None, parent
=None):
243 url
= _server_ask(self
.server_widget
)
245 if self
.lang_widget
and url
:
246 _refresh_langlist(self
.lang_widget
, url
)
247 self
.set_sensitive(True)
249 self
.set_sensitive(False)
253 def __init__(self
, sig_login
, terp_main
):
254 self
.dialog
= glade
.XML(common
.terp_path("openerp.glade"), "win_createdb", gettext
.textdomain())
255 self
.sig_login
= sig_login
256 self
.terp_main
= terp_main
258 def entry_changed(self
, *args
):
259 up1
= self
.dialog
.get_widget('ent_user_pass1').get_text()
260 up2
= self
.dialog
.get_widget('ent_user_pass2').get_text()
261 self
.dialog
.get_widget('button_db_ok').set_sensitive(bool(up1
and (up1
==up2
)))
263 def run(self
, parent
=None):
264 win
= self
.dialog
.get_widget('win_createdb')
265 self
.dialog
.signal_connect('on_ent_user_pass1_changed', self
.entry_changed
)
266 self
.dialog
.signal_connect('on_ent_user_pass2_changed', self
.entry_changed
)
267 win
.set_default_response(gtk
.RESPONSE_OK
)
269 parent
= service
.LocalService('gui.main').window
270 win
.set_transient_for(parent
)
273 pass_widget
= self
.dialog
.get_widget('ent_password_new')
274 self
.server_widget
= self
.dialog
.get_widget('ent_server_new')
275 change_button
= self
.dialog
.get_widget('but_server_new')
276 self
.lang_widget
= self
.dialog
.get_widget('db_create_combo')
277 self
.db_widget
= self
.dialog
.get_widget('ent_db_new')
278 demo_widget
= self
.dialog
.get_widget('check_demo')
279 demo_widget
.set_active(True)
281 change_button
.connect_after('clicked', self
.server_change
, win
)
282 protocol
= options
.options
['login.protocol']
283 url
= '%s%s:%s' % (protocol
, options
.options
['login.server'], options
.options
['login.port'])
285 self
.server_widget
.set_text(url
)
286 liststore
= gtk
.ListStore(str, str)
287 self
.lang_widget
.set_model(liststore
)
289 _refresh_langlist(self
.lang_widget
, url
)
291 self
.set_sensitive(False)
295 db_name
= self
.db_widget
.get_text()
296 if (res
==gtk
.RESPONSE_OK
) and (db_name
in ('table','new','create','as','select','from','where','in','inner','outer','join','group')):
297 common
.warning(_("Sorry,'" +db_name
+ "' cannot be the name of the database,it's a Reserved Keyword."), _('Bad database name !'), parent
=parent
)
299 if (res
==gtk
.RESPONSE_OK
) and ((not db_name
) or (not re
.match('^[a-zA-Z][a-zA-Z0-9_]+$', db_name
))):
300 common
.warning(_('The database name must contain only normal characters or "_".\nYou must avoid all accents, space or special characters.'), _('Bad database name !'), parent
=parent
)
304 demo_data
= demo_widget
.get_active()
306 langidx
= self
.lang_widget
.get_active_iter()
307 langreal
= langidx
and self
.lang_widget
.get_model().get_value(langidx
,1)
308 passwd
= pass_widget
.get_text()
309 user_pass
= self
.dialog
.get_widget('ent_user_pass1').get_text()
310 url
= self
.server_widget
.get_text()
311 m
= re
.match('^(http[s]?://|socket://)([\w.\-]+):(\d{1,5})$', url
or '')
313 options
.options
['login.server'] = m
.group(2)
314 options
.options
['login.port'] = m
.group(3)
315 options
.options
['login.protocol'] = m
.group(1)
316 options
.options
['login.db'] = db_name
320 if res
== gtk
.RESPONSE_OK
:
322 id=rpc
.session
.db_exec(url
, 'list')
324 raise Exception('DbExist')
325 id = rpc
.session
.db_exec(url
, 'create', passwd
, db_name
, demo_data
, langreal
, user_pass
)
326 win
= gtk
.Window(type=gtk
.WINDOW_TOPLEVEL
)
327 win
.set_position(gtk
.WIN_POS_CENTER_ON_PARENT
)
328 win
.set_title(_('OpenERP Database Installation'))
329 vbox
= gtk
.VBox(False, 0)
330 hbox
= gtk
.HBox(False, 13)
331 hbox
.set_border_width(10)
333 img
.set_from_stock('gtk-dialog-info', gtk
.ICON_SIZE_DIALOG
)
334 hbox
.pack_start(img
, expand
=True, fill
=False)
335 vbox2
= gtk
.VBox(False, 0)
337 label
.set_markup(_('<b>Operation in progress</b>'))
338 label
.set_alignment(0.0, 0.5)
339 vbox2
.pack_start(label
, expand
=True, fill
=False)
340 vbox2
.pack_start(gtk
.HSeparator(), expand
=True, fill
=True)
341 vbox2
.pack_start(gtk
.Label(_("Please wait,\nthis operation may take a while...")), expand
=True, fill
=False)
342 hbox
.pack_start(vbox2
, expand
=True, fill
=True)
343 vbox
.pack_start(hbox
)
344 pb
= gtk
.ProgressBar()
345 pb
.set_orientation(gtk
.PROGRESS_LEFT_TO_RIGHT
)
346 vbox
.pack_start(pb
, expand
=True, fill
=False)
349 parent
= service
.LocalService('gui.main').window
350 win
.set_transient_for(parent
)
352 self
.timer
= gobject
.timeout_add(1000, self
.progress_timeout
, pb
, url
, passwd
, id, win
, db_name
, parent
)
354 if e
.args
== ('DbExist',):
355 common
.warning(_("Could not create database."),_('Database already exists !'))
356 elif ('faultString' in e
and e
.faultString
=='AccessDenied:None') or str(e
)=='AccessDenied':
357 common
.warning(_('Bad database administrator password !'), _("Could not create database."))
359 common
.warning(_("Could not create database."),_('Error during database creation !'))
361 def progress_timeout(self
, pbar
, url
, passwd
, id, win
, dbname
, parent
=None):
363 progress
,users
= rpc
.session
.db_exec_no_except(url
, 'get_progress', passwd
, id)
366 common
.warning(_("The server crashed during installation.\nWe suggest you to drop this database."),_("Error during database creation !"))
373 pwdlst
= '\n'.join(map(lambda x
: ' - %s: %s / %s' % (x
['name'],x
['login'],x
['password']), users
))
374 dialog
= glade
.XML(common
.terp_path("openerp.glade"), "dia_dbcreate_ok", gettext
.textdomain())
375 win
= dialog
.get_widget('dia_dbcreate_ok')
377 parent
= service
.LocalService('gui.main').window
378 win
.set_transient_for(parent
)
380 buffer = dialog
.get_widget('dia_tv').get_buffer()
382 buffer.delete(buffer.get_start_iter(), buffer.get_end_iter())
383 iter_start
= buffer.get_start_iter()
384 buffer.insert(iter_start
, _('The following users have been installed on your database:')+'\n\n'+ pwdlst
+ '\n\n'+_('You can now connect to the database as an administrator.'))
389 if res
== gtk
.RESPONSE_OK
:
390 m
= re
.match('^(http[s]?://|socket://)([\w.]+):(\d{1,5})$', url
)
393 if x
['login']=='admin' and m
:
394 res
= [x
['login'], x
['password']]
395 res
.append( m
.group(2) )
396 res
.append( m
.group(3) )
397 res
.append( m
.group(1) )
399 log_response
= rpc
.session
.login(*res
)
400 if log_response
== 1:
401 options
.options
['login.login'] = x
['login']
402 id = self
.terp_main
.sig_win_menu(quiet
=False)
406 self
.sig_login(dbname
=dbname
)
414 class terp_main(service
.Service
):
415 def __init__(self
, name
='gui.main', audience
='gui.*'):
416 service
.Service
.__init
__(self
, name
, audience
)
417 self
.exportMethod(self
.win_add
)
419 self
._handler
_ok
= True
420 self
.glade
= glade
.XML(common
.terp_path("openerp.glade"),"win_main",gettext
.textdomain())
421 self
.status_bar_main
= self
.glade
.get_widget('hbox_status_main')
422 self
.toolbar
= self
.glade
.get_widget('main_toolbar')
423 self
.sb_requests
= self
.glade
.get_widget('sb_requests')
424 self
.sb_username
= self
.glade
.get_widget('sb_user_name')
425 self
.sb_servername
= self
.glade
.get_widget('sb_user_server')
426 id = self
.sb_servername
.get_context_id('message')
427 self
.sb_servername
.push(id, _('Press Ctrl+O to login'))
428 self
.secure_img
= self
.glade
.get_widget('secure_img')
429 self
.secure_img
.hide()
431 window
= self
.glade
.get_widget('win_main')
432 window
.connect("destroy", self
.sig_quit
)
433 window
.connect("delete_event", self
.sig_delete
)
435 self
.window
.set_icon(common
.OPENERP_ICON
)
437 self
.notebook
= gtk
.Notebook()
438 self
.notebook
.popup_enable()
439 self
.notebook
.set_scrollable(True)
440 self
.sig_id
= self
.notebook
.connect_after('switch-page', self
._sig
_page
_changt
)
441 vbox
= self
.glade
.get_widget('vbox_main')
442 vbox
.pack_start(self
.notebook
, expand
=True, fill
=True)
444 self
.shortcut_menu
= self
.glade
.get_widget('shortcut')
447 # Code to add themes to the options->theme menu
450 menu
= self
.glade
.get_widget('menu_theme')
452 themes_path
= common
.terp_path('themes')
454 for dname
in os
.listdir(themes_path
):
455 if dname
.startswith('.'):
457 fname
= common
.terp_path(os
.path
.join('themes', dname
, 'gtkrc'))
458 if fname
and os
.path
.isfile(fname
):
459 open_item
= gtk
.RadioMenuItem(old
, dname
)
461 submenu
.append(open_item
)
462 if dname
== options
.options
['client.theme']:
463 open_item
.set_active(True)
464 open_item
.connect('toggled', self
.theme_select
, dname
)
466 submenu
.append(gtk
.SeparatorMenuItem())
467 open_item
= gtk
.RadioMenuItem(old
, _('Default Theme'))
468 submenu
.append(open_item
)
469 if 'none'==options
.options
['client.theme']:
470 open_item
.set_active(True)
471 open_item
.connect('toggled', self
.theme_select
, 'none')
473 menu
.set_submenu(submenu
)
481 self
.current_page
= 0
485 'on_login_activate': self
.sig_login
,
486 'on_logout_activate': self
.sig_logout
,
487 'on_win_next_activate': self
.sig_win_next
,
488 'on_win_prev_activate': self
.sig_win_prev
,
489 'on_plugin_execute_activate': self
.sig_plugin_execute
,
490 'on_quit_activate': self
.sig_close
,
491 'on_but_menu_clicked': self
.sig_win_menu
,
492 'on_win_new_activate': self
.sig_win_menu
,
493 'on_win_home_activate': self
.sig_home_new
,
494 'on_win_close_activate': self
.sig_win_close
,
495 'on_support_activate': common
.support
,
496 'on_preference_activate': self
.sig_user_preferences
,
497 'on_read_requests_activate': self
.sig_request_open
,
498 'on_send_request_activate': self
.sig_request_new
,
499 'on_request_wait_activate': self
.sig_request_wait
,
500 'on_opt_save_activate': lambda x
: options
.options
.save(),
501 'on_menubar_icons_activate': lambda x
: self
.sig_menubar('icons'),
502 'on_menubar_text_activate': lambda x
: self
.sig_menubar('text'),
503 'on_menubar_both_activate': lambda x
: self
.sig_menubar('both'),
504 'on_mode_normal_activate': lambda x
: self
.sig_mode_change(False),
505 'on_mode_pda_activate': lambda x
: self
.sig_mode_change(True),
506 'on_opt_form_tab_top_activate': lambda x
: self
.sig_form_tab('top'),
507 'on_opt_form_tab_left_activate': lambda x
: self
.sig_form_tab('left'),
508 'on_opt_form_tab_right_activate': lambda x
: self
.sig_form_tab('right'),
509 'on_opt_form_tab_bottom_activate': lambda x
: self
.sig_form_tab('bottom'),
510 'on_opt_form_tab_orientation_horizontal_activate': lambda x
: self
.sig_form_tab_orientation(0),
511 'on_opt_form_tab_orientation_vertical_activate': lambda x
: self
.sig_form_tab_orientation(90),
512 'on_help_index_activate': self
.sig_help_index
,
513 'on_help_contextual_activate': self
.sig_help_context
,
514 'on_help_licence_activate': self
.sig_licence
,
515 'on_about_activate': self
.sig_about
,
516 'on_shortcuts_activate' : self
.sig_shortcuts
,
517 'on_db_new_activate': self
.sig_db_new
,
518 'on_db_restore_activate': self
.sig_db_restore
,
519 'on_db_backup_activate': self
.sig_db_dump
,
520 'on_db_drop_activate': self
.sig_db_drop
,
521 'on_admin_password_activate': self
.sig_db_password
,
522 'on_extension_manager_activate': self
.sig_extension_manager
,
525 self
.glade
.signal_connect(signal
, dict[signal
])
528 for button
in ('but_new', 'but_save', 'but_remove', 'but_search', 'but_previous', 'but_next', 'but_action', 'but_open', 'but_print', 'but_close', 'but_reload', 'but_switch','but_attach', 'radio_tree','radio_form','radio_graph','radio_calendar'):
529 self
.glade
.signal_connect('on_'+button
+'_clicked', self
._sig
_child
_call
, button
)
530 self
.buttons
[button
]=self
.glade
.get_widget(button
)
533 'form_del': 'but_remove',
534 'form_new': 'but_new',
535 'form_copy': 'but_copy',
536 'form_reload': 'but_reload',
537 'form_log': 'but_log',
538 'form_open': 'but_open',
539 'form_search': 'but_search',
540 'form_previous': 'but_previous',
541 'form_next': 'but_next',
542 'form_save': 'but_save',
543 'goto_id': 'but_goto_id',
544 'form_print': 'but_print',
545 'form_print_html': 'but_print_html',
546 'form_save_as': 'but_save_as',
547 'form_import': 'but_import',
548 'form_filter': 'but_filter',
549 'form_repeat': 'but_print_repeat'
552 self
.glade
.signal_connect('on_'+menu
+'_activate', self
._sig
_child
_call
, menus
[menu
])
554 spool
= service
.LocalService('spool')
555 spool
.subscribe('gui.window', self
.win_add
)
558 # we now create the icon for the attachment button when there are attachments
559 self
.__img
_no
_attachments
= gtk
.Image()
560 pxbf
= self
.window
.render_icon(self
.buttons
['but_attach'].get_stock_id(), self
.toolbar
.get_icon_size())
561 self
.__img
_no
_attachments
.set_from_pixbuf(pxbf
)
562 self
.__img
_no
_attachments
.show()
565 w
, h
= pxbf
.get_width(), pxbf
.get_height()
566 overlay
= self
.window
.render_icon(gtk
.STOCK_APPLY
, gtk
.ICON_SIZE_MENU
)
567 ow
, oh
= overlay
.get_width(), overlay
.get_height()
568 overlay
.composite(pxbf
,
573 gtk
.gdk
.INTERP_NEAREST
,
576 self
.__img
_attachments
= gtk
.Image()
577 self
.__img
_attachments
.set_from_pixbuf(pxbf
)
578 self
.__img
_attachments
.show()
582 settings
= gtk
.settings_get_default()
583 settings
.set_long_property('gtk-button-images', 1, 'TinyERP:gui.main')
585 def fnc_menuitem(menuitem
, opt_name
):
586 options
.options
[opt_name
] = menuitem
.get_active()
588 'on_opt_print_preview_activate': (fnc_menuitem
, 'printer.preview', 'opt_print_preview'),
589 'on_opt_form_toolbar_activate': (fnc_menuitem
, 'form.toolbar', 'opt_form_toolbar'),
591 self
.glade
.get_widget('menubar_'+(options
.options
['client.toolbar'] or 'both')).set_active(True)
592 self
.sig_menubar(options
.options
['client.toolbar'] or 'both')
593 self
.glade
.get_widget('opt_form_tab_'+(options
.options
['client.form_tab'] or 'left')).set_active(True)
594 self
.sig_form_tab(options
.options
['client.form_tab'] or 'left')
595 self
.glade
.get_widget('opt_form_tab_orientation_'+(str(options
.options
['client.form_tab_orientation']) or '0')).set_active(True)
596 self
.sig_form_tab_orientation(options
.options
['client.form_tab_orientation'] or 0)
597 if options
.options
['client.modepda']:
598 self
.glade
.get_widget('mode_pda').set_active(True)
600 self
.glade
.get_widget('mode_normal').set_active(True)
603 self
.glade
.signal_connect(signal
, dict[signal
][0], dict[signal
][1])
604 self
.glade
.get_widget(dict[signal
][2]).set_active(int(bool(options
.options
[dict[signal
][1]])))
606 # Adding a timer the check to requests
607 gobject
.timeout_add(5 * 60 * 1000, self
.request_set
)
609 def shortcut_edit(self
, widget
, model
='ir.ui.menu'):
610 obj
= service
.LocalService('gui.window')
611 domain
= [('user_id', '=', rpc
.session
.uid
), ('resource', '=', model
)]
612 obj
.create(False, 'ir.ui.view_sc', res_id
=None, domain
=domain
, view_type
='form', mode
='tree,form')
614 def shortcut_set(self
, sc
=None):
615 def _action_shortcut(widget
, action
):
617 ctx
= rpc
.session
.context
.copy()
618 obj
= service
.LocalService('action.main')
619 obj
.exec_keyword('tree_but_open', {'model': 'ir.ui.menu', 'id': action
[0],
620 'ids': [action
[0]], 'report_type': 'pdf', 'window': self
.window
}, context
=ctx
)
623 uid
= rpc
.session
.uid
624 sc
= rpc
.session
.rpc_exec_auth('/object', 'execute', 'ir.ui.view_sc', 'get_sc', uid
, 'ir.ui.menu', rpc
.session
.context
) or []
628 menuitem
= gtk
.MenuItem(s
['name'])
629 menuitem
.connect('activate', _action_shortcut
, s
['res_id'])
632 menu
.add(gtk
.SeparatorMenuItem())
633 menuitem
= gtk
.MenuItem(_('Edit'))
634 menuitem
.connect('activate', self
.shortcut_edit
)
638 self
.shortcut_menu
.set_submenu(menu
)
639 self
.shortcut_menu
.set_sensitive(True)
641 def shortcut_unset(self
):
644 self
.shortcut_menu
.set_submenu(menu
)
645 self
.shortcut_menu
.set_sensitive(False)
647 def theme_select(self
, widget
, theme
):
648 options
.options
['client.theme'] = theme
650 self
.window
.show_all()
653 def sig_mode_change(self
, pda_mode
=False):
654 options
.options
['client.modepda'] = pda_mode
655 return self
.sig_mode()
658 pda_mode
= options
.options
['client.modepda']
660 self
.status_bar_main
.hide()
662 self
.status_bar_main
.show()
665 def sig_menubar(self
, option
):
666 options
.options
['client.toolbar'] = option
668 self
.toolbar
.set_style(gtk
.TOOLBAR_BOTH
)
670 self
.toolbar
.set_style(gtk
.TOOLBAR_TEXT
)
671 elif option
=='icons':
672 self
.toolbar
.set_style(gtk
.TOOLBAR_ICONS
)
674 def sig_form_tab(self
, option
):
675 options
.options
['client.form_tab'] = option
677 def sig_form_tab_orientation(self
, option
):
678 options
.options
['client.form_tab_orientation'] = option
680 def sig_win_next(self
, args
):
681 pn
= self
.notebook
.get_current_page()
682 if pn
== len(self
.pages
)-1:
684 self
.notebook
.set_current_page(pn
+1)
686 def sig_win_prev(self
, args
):
687 pn
= self
.notebook
.get_current_page()
688 self
.notebook
.set_current_page(pn
-1)
690 def sig_user_preferences(self
, *args
):
691 win
=win_preference
.win_preference(parent
=self
.window
)
695 def sig_win_close(self
, *args
):
696 self
._sig
_child
_call
(args
[0], 'but_close')
698 def sig_request_new(self
, args
=None):
699 obj
= service
.LocalService('gui.window')
701 return obj
.create(None, 'res.request', False,
702 [('act_from', '=', rpc
.session
.uid
)], 'form',
703 mode
='form,tree', window
=self
.window
,
704 context
={'active_test': False})
708 def sig_request_open(self
, args
=None):
709 ids
,ids2
= self
.request_set()
710 obj
= service
.LocalService('gui.window')
712 return obj
.create(False, 'res.request', ids
,
713 [('act_to', '=', rpc
.session
.uid
), ('active', '=', True)],
714 'form', mode
='tree,form', window
=self
.window
,
715 context
={'active_test': False})
719 def sig_request_wait(self
, args
=None):
720 ids
,ids2
= self
.request_set()
721 obj
= service
.LocalService('gui.window')
723 return obj
.create(False, 'res.request', ids
,
724 [('act_from', '=', rpc
.session
.uid
),
725 ('state', '=', 'waiting'), ('active', '=', True)],
726 'form', mode
='tree,form', window
=self
.window
,
727 context
={'active_test': False})
731 def request_set(self
):
733 uid
= rpc
.session
.uid
734 ids
,ids2
= rpc
.session
.rpc_exec_auth_try('/object', 'execute',
735 'res.request', 'request_get')
737 message
= _('%s request(s)') % len(ids
)
739 message
= _('No request')
741 message
+= _(' - %s request(s) sended') % len(ids2
)
742 id = self
.sb_requests
.get_context_id('message')
743 self
.sb_requests
.push(id, message
)
748 def sig_login(self
, widget
=None, dbname
=False):
750 RES_BAD_PASSWORD
= -2
753 log_response
= RES_BAD_PASSWORD
755 while log_response
== RES_BAD_PASSWORD
:
758 res
= l
.run(dbname
=dbname
, parent
=self
.window
)
760 if e
.args
== ('QueryCanceled',):
763 service
.LocalService('gui.main').window
.present()
764 self
.sig_logout(widget
)
765 log_response
= rpc
.session
.login(*res
)
766 if log_response
== RES_OK
:
767 options
.options
.save()
768 id = self
.sig_win_menu(quiet
=False)
770 self
.sig_home_new(quiet
=True, except_id
=id)
771 if res
[4] == 'https://':
772 self
.secure_img
.show()
774 self
.secure_img
.hide()
776 elif log_response
== RES_CNX_ERROR
:
777 common
.message(_('Connection error !\nUnable to connect to the server !'))
778 elif log_response
== RES_BAD_PASSWORD
:
779 common
.message(_('Connection error !\nBad username or password !'))
780 except rpc
.rpc_exception
:
783 self
.glade
.get_widget('but_menu').set_sensitive(True)
784 self
.glade
.get_widget('user').set_sensitive(True)
785 self
.glade
.get_widget('form').set_sensitive(True)
786 self
.glade
.get_widget('plugins').set_sensitive(True)
789 def sig_logout(self
, widget
):
792 wid
= self
._wid
_get
()
794 if 'but_close' in wid
.handlers
:
795 res
= wid
.handlers
['but_close']()
798 res
= self
._win
_del
()
801 id = self
.sb_requests
.get_context_id('message')
802 self
.sb_requests
.push(id, '')
803 id = self
.sb_username
.get_context_id('message')
804 self
.sb_username
.push(id, _('Not logged !'))
805 id = self
.sb_servername
.get_context_id('message')
806 self
.sb_servername
.push(id, _('Press Ctrl+O to login'))
807 self
.secure_img
.hide()
808 self
.shortcut_unset()
809 self
.glade
.get_widget('but_menu').set_sensitive(False)
810 self
.glade
.get_widget('user').set_sensitive(False)
811 self
.glade
.get_widget('form').set_sensitive(False)
812 self
.glade
.get_widget('plugins').set_sensitive(False)
816 def sig_help_index(self
, widget
):
817 tools
.launch_browser(options
.options
['help.index'])
819 def sig_help_context(self
, widget
):
820 model
= self
._wid
_get
().model
821 l
= rpc
.session
.context
.get('lang','en_US')
822 tools
.launch_browser(options
.options
['help.context']+'?model=%s&lang=%s' % (model
,l
))
824 def sig_licence(self
, widget
):
825 dialog
= glade
.XML(common
.terp_path("openerp.glade"), "win_licence", gettext
.textdomain())
826 dialog
.signal_connect("on_but_ok_pressed", lambda obj
: dialog
.get_widget('win_licence').destroy())
828 win
= dialog
.get_widget('win_licence')
829 win
.set_transient_for(self
.window
)
832 def sig_about(self
, widget
):
833 about
= glade
.XML(common
.terp_path("openerp.glade"), "win_about", gettext
.textdomain())
834 buffer = about
.get_widget('textview2').get_buffer()
835 about_txt
= buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter())
836 buffer.set_text(about_txt
% openerp_version
)
837 about
.signal_connect("on_but_ok_pressed", lambda obj
: about
.get_widget('win_about').destroy())
839 win
= about
.get_widget('win_about')
840 win
.set_transient_for(self
.window
)
843 def sig_shortcuts(self
, widget
):
844 shortcuts_win
= glade
.XML(common
.terp_path('openerp.glade'), 'shortcuts_dia', gettext
.textdomain())
845 shortcuts_win
.signal_connect("on_but_ok_pressed", lambda obj
: shortcuts_win
.get_widget('shortcuts_dia').destroy())
847 win
= shortcuts_win
.get_widget('shortcuts_dia')
848 win
.set_transient_for(self
.window
)
851 def sig_win_menu(self
, widget
=None, quiet
=True):
852 for p
in range(len(self
.pages
)):
853 if self
.pages
[p
].model
=='ir.ui.menu':
854 self
.notebook
.set_current_page(p
)
856 res
= self
.sig_win_new(widget
, type='menu_id', quiet
=quiet
)
858 return self
.sig_win_new(widget
, type='action_id', quiet
=quiet
)
861 def sig_win_new(self
, widget
=None, type='menu_id', quiet
=True, except_id
=False):
863 act_id
= rpc
.session
.rpc_exec_auth('/object', 'execute', 'res.users',
864 'read', [rpc
.session
.uid
], [type,'name'], rpc
.session
.context
)
867 id = self
.sb_username
.get_context_id('message')
868 self
.sb_username
.push(id, act_id
[0]['name'] or '')
869 id = self
.sb_servername
.get_context_id('message')
870 data
= urlparse
.urlsplit(rpc
.session
._url
)
871 self
.sb_servername
.push(id, data
[0]+':'+(data
[1] and '//'+data
[1] \
872 or data
[2])+' ['+options
.options
['login.db']+']')
873 if not act_id
[0][type]:
876 common
.warning(_("You can not log into the system !\nAsk the administrator to verify\nyou have an action defined for your user."),'Access Denied !')
879 act_id
= act_id
[0][type][0]
880 if except_id
and act_id
== except_id
:
882 obj
= service
.LocalService('action.main')
883 win
= obj
.execute(act_id
, {'window':self
.window
})
885 user
= rpc
.session
.rpc_exec_auth_wo('/object', 'execute', 'res.users',
886 'read', [rpc
.session
.uid
], [type,'name'], rpc
.session
.context
)
888 act_id
= user
[0][type][0]
893 def sig_home_new(self
, widget
=None, quiet
=True, except_id
=False):
894 return self
.sig_win_new(widget
, type='action_id', quiet
=quiet
,
897 def sig_plugin_execute(self
, widget
):
899 pn
= self
.notebook
.get_current_page()
900 datas
= {'model': self
.pages
[pn
].model
, 'ids':self
.pages
[pn
].ids_get(), 'id' : self
.pages
[pn
].id_get()}
901 plugins
.execute(datas
)
903 def sig_quit(self
, widget
):
904 options
.options
.save()
907 def sig_close(self
, widget
):
908 if common
.sur(_("Do you really want to quit ?"), parent
=self
.window
):
909 if not self
.sig_logout(widget
):
911 options
.options
.save()
914 def sig_delete(self
, widget
, event
, data
=None):
915 if common
.sur(_("Do you really want to quit ?"), parent
=self
.window
):
916 if not self
.sig_logout(widget
):
921 def win_add(self
, win
, datas
):
922 self
.pages
.append(win
)
923 self
.notebook
.append_page(win
.widget
, gtk
.Label(win
.name
))
924 self
.notebook
.set_current_page(-1)
926 def message(self
, message
):
927 id = self
.status_bar
.get_context_id('message')
928 self
.status_bar
.push(id, message
)
930 def __attachment_callback(self
, view
, objid
):
931 current_view
= self
._wid
_get
()
932 current_id
= current_view
and current_view
.id_get()
933 if current_view
== view
and objid
== current_id
:
935 if objid
and view
.screen
.current_view
.view_type
== 'form':
936 cpt
= rpc
.session
.rpc_exec_auth('/object', 'execute',
937 'ir.attachment', 'search_count',
938 [('res_model', '=', view
.model
), ('res_id', '=', objid
)])
940 self
.buttons
['but_attach'].set_icon_widget(self
.__img
_attachments
)
941 self
.buttons
['but_attach'].set_label(_('Attachments (%d)') % cpt
)
944 def _update_attachment_button(self
, view
= None):
946 Update the attachment icon for display the number of attachments
949 view
= self
._wid
_get
()
951 id = view
and view
.id_get()
952 gobject
.timeout_add(1500, self
.__attachment
_callback
, view
, id)
953 self
.buttons
['but_attach'].set_icon_widget(self
.__img
_no
_attachments
)
954 self
.buttons
['but_attach'].set_label(_('Attachments'))
957 def sb_set(self
, view
=None):
959 view
= self
._wid
_get
()
960 if view
and hasattr(view
, 'screen'):
961 self
._handler
_ok
= False
962 self
.glade
.get_widget('radio_'+view
.screen
.current_view
.view_type
).set_active(True)
963 self
._handler
_ok
= True
964 self
._update
_attachment
_button
(view
)
965 for x
in self
.buttons
:
967 self
.buttons
[x
].set_sensitive(view
and (x
in view
.handlers
))
970 pn
= self
.notebook
.get_current_page()
972 self
.notebook
.disconnect(self
.sig_id
)
973 page
= self
.pages
.pop(pn
)
974 self
.notebook
.remove_page(pn
)
975 self
.sig_id
= self
.notebook
.connect_after('switch-page', self
._sig
_page
_changt
)
980 return self
.notebook
.get_current_page() != -1
983 pn
= self
.notebook
.get_current_page()
986 return self
.pages
[pn
]
988 def _sig_child_call(self
, widget
, button_name
, *args
):
989 if not self
._handler
_ok
:
991 wid
= self
._wid
_get
()
994 if button_name
.startswith('radio_'):
995 act
= self
.glade
.get_widget(button_name
).get_active()
996 if not act
: return False
998 if button_name
in wid
.handlers
:
999 res
= wid
.handlers
[button_name
]()
1000 # for those buttons, we refresh the attachment button.
1001 # for the "switch view" button, the action has already
1002 # been called by the Screen object of the view (wid)
1003 if button_name
in ('but_new', 'but_remove', 'but_search', \
1004 'but_previous', 'but_next', 'but_open', \
1005 'but_close', 'but_reload', 'but_attach', 'but_goto_id'):
1006 self
._update
_attachment
_button
(wid
)
1007 if button_name
.startswith('radio_'):
1009 #mode = wid.screen.current_view.view_type
1010 #print 'RADIO CALLED', button_name, args, self.glade.get_widget(button_name).get_active(), button_name[6:]
1011 #if self.glade.get_widget(button_name).get_active() and (mode<>button_name[6:]):
1012 # print 'SWITCH', button_name
1014 if button_name
=='but_close' and res
:
1017 def _sig_page_changt(self
, widget
=None, *args
):
1018 self
.last_page
= self
.current_page
1019 self
.current_page
= self
.notebook
.get_current_page()
1022 def sig_db_new(self
, widget
):
1023 if not self
.sig_logout(widget
):
1025 dia
= db_create(self
.sig_login
, self
)
1026 res
= dia
.run(self
.window
)
1028 options
.options
.save()
1031 def sig_db_drop(self
, widget
):
1032 if not self
.sig_logout(widget
):
1034 url
, db_name
, passwd
= self
._choose
_db
_select
(_('Delete a database'))
1039 rpc
.session
.db_exec(url
, 'drop', passwd
, db_name
)
1040 common
.message(_("Database dropped successfully !"), parent
=self
.window
)
1041 except Exception, e
:
1042 if ('faultString' in e
and e
.faultString
=='AccessDenied:None') or str(e
)=='AccessDenied':
1043 common
.warning(_('Bad database administrator password !'),_("Could not drop database."), parent
=self
.window
)
1045 common
.warning(_("Couldn't drop database"), parent
=self
.window
)
1047 def sig_db_restore(self
, widget
):
1048 filename
= common
.file_selection(_('Open...'), parent
=self
.window
, preview
=False)
1052 url
, db_name
, passwd
= self
._choose
_db
_ent
()
1055 f
= file(filename
, 'rb')
1056 data_b64
= base64
.encodestring(f
.read())
1058 rpc
.session
.db_exec(url
, 'restore', passwd
, db_name
, data_b64
)
1059 common
.message(_("Database restored successfully !"), parent
=self
.window
)
1061 if ('faultString' in e
and e
.faultString
=='AccessDenied:None') or str(e
)=='AccessDenied':
1062 common
.warning(_('Bad database administrator password !'),_("Could not restore database."), parent
=self
.window
)
1064 common
.warning(_("Couldn't restore database"), parent
=self
.window
)
1066 def sig_extension_manager(self
,widget
):
1067 win
= win_extension
.win_extension(self
.window
)
1070 def sig_db_password(self
, widget
):
1071 dialog
= glade
.XML(common
.terp_path("openerp.glade"), "dia_passwd_change",
1072 gettext
.textdomain())
1073 win
= dialog
.get_widget('dia_passwd_change')
1074 win
.set_icon(common
.OPENERP_ICON
)
1075 win
.set_transient_for(self
.window
)
1077 server_widget
= dialog
.get_widget('ent_server2')
1078 old_pass_widget
= dialog
.get_widget('old_passwd')
1079 new_pass_widget
= dialog
.get_widget('new_passwd')
1080 new_pass2_widget
= dialog
.get_widget('new_passwd2')
1081 change_button
= dialog
.get_widget('but_server_change1')
1082 change_button
.connect_after('clicked', lambda a
,b
: _server_ask(b
, win
), server_widget
)
1084 host
= options
.options
['login.server']
1085 port
= options
.options
['login.port']
1086 protocol
= options
.options
['login.protocol']
1087 url
= '%s%s:%s' % (protocol
, host
, port
)
1088 server_widget
.set_text(url
)
1091 if res
== gtk
.RESPONSE_OK
:
1092 url
= server_widget
.get_text()
1093 old_passwd
= old_pass_widget
.get_text()
1094 new_passwd
= new_pass_widget
.get_text()
1095 new_passwd2
= new_pass2_widget
.get_text()
1096 if new_passwd
!= new_passwd2
:
1097 common
.warning(_("Confirmation password do not match " \
1098 "new password, operation cancelled!"),
1099 _("Validation Error."), parent
=win
)
1102 rpc
.session
.db_exec(url
, 'change_admin_password',
1103 old_passwd
, new_passwd
)
1105 if ('faultString' in e
and e
.faultString
=='AccessDenied:None') \
1106 or str(e
)=='AccessDenied':
1107 common
.warning(_("Could not change password database."),
1108 _('Bas password provided !'), parent
=win
)
1110 common
.warning(_("Error, password not changed."),
1112 self
.window
.present()
1115 def sig_db_dump(self
, widget
):
1116 url
, db_name
, passwd
= self
._choose
_db
_select
(_('Backup a database'))
1119 filename
= common
.file_selection(_('Save As...'),
1120 action
=gtk
.FILE_CHOOSER_ACTION_SAVE
, parent
=self
.window
, preview
=False)
1124 dump_b64
= rpc
.session
.db_exec(url
, 'dump', passwd
, db_name
)
1125 dump
= base64
.decodestring(dump_b64
)
1126 f
= file(filename
, 'wb')
1129 common
.message(_("Database backuped successfully !"), parent
=self
.window
)
1131 common
.warning(_("Couldn't backup database."), parent
=self
.window
)
1133 def _choose_db_select(self
, title
=_("Backup a database")):
1134 def refreshlist(widget
, db_widget
, label
, url
):
1135 res
= _refresh_dblist(db_widget
, url
)
1137 label
.set_label('<b>'+_('Could not connect to server !')+'</b>')
1141 label
.set_label('<b>'+_('No database found, you must create one !')+'</b>')
1149 def refreshlist_ask(widget
, server_widget
, db_widget
, label
, parent
=None):
1150 url
= _server_ask(server_widget
, parent
)
1153 refreshlist(widget
, db_widget
, label
, url
)
1156 dialog
= glade
.XML(common
.terp_path("openerp.glade"), "win_db_select",
1157 gettext
.textdomain())
1158 win
= dialog
.get_widget('win_db_select')
1159 win
.set_icon(common
.OPENERP_ICON
)
1160 win
.set_default_response(gtk
.RESPONSE_OK
)
1161 win
.set_transient_for(self
.window
)
1164 pass_widget
= dialog
.get_widget('ent_passwd_select')
1165 server_widget
= dialog
.get_widget('ent_server_select')
1166 db_widget
= dialog
.get_widget('combo_db_select')
1167 label
= dialog
.get_widget('label_db_select')
1170 dialog
.get_widget('db_select_label').set_markup('<b>'+title
+'</b>')
1172 protocol
= options
.options
['login.protocol']
1173 url
= '%s%s:%s' % (protocol
, options
.options
['login.server'], options
.options
['login.port'])
1174 server_widget
.set_text(url
)
1176 liststore
= gtk
.ListStore(str)
1177 db_widget
.set_model(liststore
)
1179 refreshlist(None, db_widget
, label
, url
)
1180 change_button
= dialog
.get_widget('but_server_select')
1181 change_button
.connect_after('clicked', refreshlist_ask
, server_widget
, db_widget
, label
, win
)
1183 cell
= gtk
.CellRendererText()
1184 db_widget
.pack_start(cell
, True)
1185 db_widget
.add_attribute(cell
, 'text', 0)
1192 if res
== gtk
.RESPONSE_OK
:
1193 db
= db_widget
.get_active_text()
1194 url
= server_widget
.get_text()
1195 passwd
= pass_widget
.get_text()
1196 self
.window
.present()
1198 return (url
,db
,passwd
)
1200 def _choose_db_ent(self
):
1201 dialog
= glade
.XML(common
.terp_path("openerp.glade"), "win_db_ent",
1202 gettext
.textdomain())
1203 win
= dialog
.get_widget('win_db_ent')
1204 win
.set_icon(common
.OPENERP_ICON
)
1205 win
.set_transient_for(self
.window
)
1208 db_widget
= dialog
.get_widget('ent_db')
1209 widget_pass
= dialog
.get_widget('ent_password')
1210 widget_url
= dialog
.get_widget('ent_server1')
1212 protocol
= options
.options
['login.protocol']
1213 url
= '%s%s:%s' % (protocol
, options
.options
['login.server'],
1214 options
.options
['login.port'])
1215 widget_url
.set_text(url
)
1217 change_button
= dialog
.get_widget('but_server_change')
1218 change_button
.connect_after('clicked', lambda a
,b
: _server_ask(b
, win
),
1226 if res
== gtk
.RESPONSE_OK
:
1227 db
= db_widget
.get_text()
1228 url
= widget_url
.get_text()
1229 passwd
= widget_pass
.get_text()
1230 self
.window
.present()
1232 return url
, db
, passwd
1235 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: