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 ##############################################################################
46 from window
import win_preference
, win_extension
52 def _refresh_dblist(db_widget
, url
, dbtoload
=None):
54 dbtoload
= options
.options
['login.db']
56 liststore
= db_widget
.get_model()
58 result
= rpc
.session
.list_db(url
)
61 for db_num
, db_name
in enumerate(rpc
.session
.list_db(url
)):
62 liststore
.append([db_name
])
63 if db_name
== dbtoload
:
65 db_widget
.set_active(index
)
68 def _refresh_langlist(lang_widget
, url
):
69 liststore
= lang_widget
.get_model()
71 lang_list
= rpc
.session
.db_exec_no_except(url
, 'list_lang')
72 lang_list
.append( ('en_US','English') )
73 for key
,val
in lang_list
:
74 liststore
.insert(0, (val
,key
))
75 lang_widget
.set_active(0)
78 def _server_ask(server_widget
, parent
=None):
80 win_gl
= glade
.XML(common
.terp_path("terp.glade"),"win_server",gettext
.textdomain())
81 win
= win_gl
.get_widget('win_server')
83 parent
= service
.LocalService('gui.main').window
84 win
.set_transient_for(parent
)
85 win
.set_icon(common
.TINYERP_ICON
)
87 win
.set_default_response(gtk
.RESPONSE_OK
)
88 host_widget
= win_gl
.get_widget('ent_host')
89 port_widget
= win_gl
.get_widget('ent_port')
90 protocol_widget
= win_gl
.get_widget('protocol')
92 protocol
={'XML-RPC': 'http://',
93 'XML-RPC secure': 'https://',
94 'NET-RPC (faster)': 'socket://',}
95 listprotocol
= gtk
.ListStore(str)
96 protocol_widget
.set_model(listprotocol
)
99 m
= re
.match('^(http[s]?://|socket://)([\w.-]+):(\d{1,5})$', server_widget
.get_text())
101 host_widget
.set_text(m
.group(2))
102 port_widget
.set_text(m
.group(3))
107 listprotocol
.append([p
])
108 if m
and protocol
[p
] == m
.group(1):
111 protocol_widget
.set_active(index
)
114 if res
== gtk
.RESPONSE_OK
:
115 protocol
= protocol
[protocol_widget
.get_active_text()]
116 url
= '%s%s:%s' % (protocol
, host_widget
.get_text(), port_widget
.get_text())
117 server_widget
.set_text(url
)
124 class db_login(object):
126 self
.win_gl
= glade
.XML(common
.terp_path("terp.glade"),"win_login",gettext
.textdomain())
128 def refreshlist(self
, widget
, db_widget
, label
, url
, butconnect
=False):
130 def check_server_version(url
):
133 full_server_version
= rpc
.session
.db_exec_no_except(url
, 'server_version')
134 server_version
= full_server_version
.split('.')
135 client_version
= release
.version
.split('.')
136 return (server_version
[:2] == client_version
[:2], full_server_version
, release
.version
)
138 # the server doesn't understand the request. It's mean that it's an old version of the server
139 return (False, _('Unknow'), release
.version
)
141 res
= _refresh_dblist(db_widget
, url
)
143 label
.set_label('<b>'+_('Could not connect to server !')+'</b>')
147 butconnect
.set_sensitive(False)
150 label
.set_label('<b>'+_('No database found, you must create one !')+'</b>')
154 butconnect
.set_sensitive(False)
159 butconnect
.set_sensitive(True)
161 is_same_version
, server_version
, client_version
= check_server_version(url
)
162 if not is_same_version
:
163 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
,))
166 def refreshlist_ask(self
,widget
, server_widget
, db_widget
, label
, butconnect
= False, url
=False, parent
=None):
167 url
= _server_ask(server_widget
, parent
) or url
168 return self
.refreshlist(widget
, db_widget
, label
, url
, butconnect
)
170 def run(self
, dbname
=None, parent
=None):
172 win
= self
.win_gl
.get_widget('win_login')
174 parent
= service
.LocalService('gui.main').window
175 win
.set_transient_for(parent
)
176 win
.set_icon(common
.TINYERP_ICON
)
178 img
= self
.win_gl
.get_widget('image_tinyerp')
179 img
.set_from_file(common
.terp_path_pixmaps('tinyerp.png'))
180 login
= self
.win_gl
.get_widget('ent_login')
181 passwd
= self
.win_gl
.get_widget('ent_passwd')
182 server_widget
= self
.win_gl
.get_widget('ent_server')
183 but_connect
= self
.win_gl
.get_widget('button_connect')
184 db_widget
= self
.win_gl
.get_widget('combo_db')
185 change_button
= self
.win_gl
.get_widget('but_server')
186 label
= self
.win_gl
.get_widget('combo_label')
189 host
= options
.options
['login.server']
190 port
= options
.options
['login.port']
191 protocol
= options
.options
['login.protocol']
193 url
= '%s%s:%s' % (protocol
, host
, port
)
194 server_widget
.set_text(url
)
195 login
.set_text(options
.options
['login.login'])
197 # construct the list of available db and select the last one used
198 liststore
= gtk
.ListStore(str)
199 db_widget
.set_model(liststore
)
200 cell
= gtk
.CellRendererText()
201 db_widget
.pack_start(cell
, True)
202 db_widget
.add_attribute(cell
, 'text', 0)
204 res
= self
.refreshlist(None, db_widget
, label
, url
, but_connect
)
205 change_button
.connect_after('clicked', self
.refreshlist_ask
, server_widget
, db_widget
, label
, but_connect
, url
, win
)
208 iter = liststore
.get_iter_root()
210 if liststore
.get_value(iter, 0)==dbname
:
211 db_widget
.set_active_iter(iter)
213 iter = liststore
.iter_next(iter)
216 m
= re
.match('^(http[s]?://|socket://)([\w.\-]+):(\d{1,5})$', server_widget
.get_text() or '')
218 options
.options
['login.server'] = m
.group(2)
219 options
.options
['login.login'] = login
.get_text()
220 options
.options
['login.port'] = m
.group(3)
221 options
.options
['login.protocol'] = m
.group(1)
222 options
.options
['login.db'] = db_widget
.get_active_text()
223 result
= (login
.get_text(), passwd
.get_text(), m
.group(2), m
.group(3), m
.group(1), db_widget
.get_active_text())
227 raise Exception('QueryCanceled')
228 if res
<> gtk
.RESPONSE_OK
:
231 raise Exception('QueryCanceled')
236 class db_create(object):
237 def set_sensitive(self
, sensitive
):
239 label
= self
.dialog
.get_widget('db_label_info')
240 label
.set_text(_('Do not use special characters !'))
241 self
.dialog
.get_widget('button_db_ok').set_sensitive(True)
243 label
= self
.dialog
.get_widget('db_label_info')
244 label
.set_markup('<b>'+_('Can not connect to server, please change it !')+'</b>')
245 self
.dialog
.get_widget('button_db_ok').set_sensitive(False)
248 def server_change(self
, widget
=None, parent
=None):
249 url
= _server_ask(self
.server_widget
)
251 if self
.lang_widget
and url
:
252 _refresh_langlist(self
.lang_widget
, url
)
253 self
.set_sensitive(True)
255 self
.set_sensitive(False)
259 def __init__(self
, sig_login
):
260 self
.dialog
= glade
.XML(common
.terp_path("terp.glade"), "win_createdb", gettext
.textdomain())
261 self
.sig_login
= sig_login
263 def run(self
, parent
=None):
264 win
= self
.dialog
.get_widget('win_createdb')
265 win
.set_default_response(gtk
.RESPONSE_OK
)
267 parent
= service
.LocalService('gui.main').window
268 win
.set_transient_for(parent
)
271 pass_widget
= self
.dialog
.get_widget('ent_password_new')
272 self
.server_widget
= self
.dialog
.get_widget('ent_server_new')
273 change_button
= self
.dialog
.get_widget('but_server_new')
274 self
.lang_widget
= self
.dialog
.get_widget('db_create_combo')
275 self
.db_widget
= self
.dialog
.get_widget('ent_db_new')
276 demo_widget
= self
.dialog
.get_widget('check_demo')
277 demo_widget
.set_active(True)
279 change_button
.connect_after('clicked', self
.server_change
, win
)
280 protocol
= options
.options
['login.protocol']
281 url
= '%s%s:%s' % (protocol
, options
.options
['login.server'], options
.options
['login.port'])
283 self
.server_widget
.set_text(url
)
284 liststore
= gtk
.ListStore(str, str)
285 self
.lang_widget
.set_model(liststore
)
287 _refresh_langlist(self
.lang_widget
, url
)
289 self
.set_sensitive(False)
293 db_name
= self
.db_widget
.get_text()
294 if (res
==gtk
.RESPONSE_OK
) and ((not db_name
) or (not re
.match('^[a-zA-Z][a-zA-Z0-9_]+$', db_name
))):
295 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
)
299 demo_data
= demo_widget
.get_active()
301 langidx
= self
.lang_widget
.get_active_iter()
302 langreal
= langidx
and self
.lang_widget
.get_model().get_value(langidx
,1)
303 passwd
= pass_widget
.get_text()
304 url
= self
.server_widget
.get_text()
305 m
= re
.match('^(http[s]?://|socket://)([\w.\-]+):(\d{1,5})$', url
or '')
307 options
.options
['login.server'] = m
.group(2)
308 options
.options
['login.port'] = m
.group(3)
309 options
.options
['login.protocol'] = m
.group(1)
313 if res
== gtk
.RESPONSE_OK
:
315 id=rpc
.session
.db_exec(url
, 'list')
317 raise Exception('DbExist')
318 id = rpc
.session
.db_exec(url
, 'create', passwd
, db_name
, demo_data
, langreal
)
319 win
= gtk
.Window(type=gtk
.WINDOW_TOPLEVEL
)
320 win
.set_position(gtk
.WIN_POS_CENTER_ON_PARENT
)
321 vbox
= gtk
.VBox(False, 0)
322 hbox
= gtk
.HBox(False, 13)
323 hbox
.set_border_width(10)
325 img
.set_from_stock('gtk-dialog-info', gtk
.ICON_SIZE_DIALOG
)
326 hbox
.pack_start(img
, expand
=True, fill
=False)
327 vbox2
= gtk
.VBox(False, 0)
329 label
.set_markup(_('<b>Operation in progress</b>'))
330 label
.set_alignment(0.0, 0.5)
331 vbox2
.pack_start(label
, expand
=True, fill
=False)
332 vbox2
.pack_start(gtk
.HSeparator(), expand
=True, fill
=True)
333 vbox2
.pack_start(gtk
.Label(_("Please wait,\nthis operation may take a while...")), expand
=True, fill
=False)
334 hbox
.pack_start(vbox2
, expand
=True, fill
=True)
335 vbox
.pack_start(hbox
)
336 pb
= gtk
.ProgressBar()
337 pb
.set_orientation(gtk
.PROGRESS_LEFT_TO_RIGHT
)
338 vbox
.pack_start(pb
, expand
=True, fill
=False)
341 parent
= service
.LocalService('gui.main').window
342 win
.set_transient_for(parent
)
344 self
.timer
= gobject
.timeout_add(1000, self
.progress_timeout
, pb
, url
, passwd
, id, win
, db_name
, parent
)
346 if e
.args
== ('DbExist',):
347 common
.warning(_("Could not create database."),_('Database already exists !'))
348 elif ('faultString' in e
and e
.faultString
=='AccessDenied:None') or str(e
)=='AccessDenied':
349 common
.warning(_('Bad database administrator password !'), _("Could not create database."))
355 common
.warning(_("Could not create database."),_('Error during database creation !'))
357 def progress_timeout(self
, pbar
, url
, passwd
, id, win
, dbname
, parent
=None):
359 progress
,users
= rpc
.session
.db_exec_no_except(url
, 'get_progress', passwd
, id)
362 common
.warning(_("The server crashed during installation.\nWe suggest you to drop this database."),_("Error during database creation !"))
369 pwdlst
= '\n'.join(map(lambda x
: ' - %s: %s / %s' % (x
['name'],x
['login'],x
['password']), users
))
370 dialog
= glade
.XML(common
.terp_path("terp.glade"), "dia_dbcreate_ok", gettext
.textdomain())
371 win
= dialog
.get_widget('dia_dbcreate_ok')
373 parent
= service
.LocalService('gui.main').window
374 win
.set_transient_for(parent
)
376 buffer = dialog
.get_widget('dia_tv').get_buffer()
378 buffer.delete(buffer.get_start_iter(), buffer.get_end_iter())
379 iter_start
= buffer.get_start_iter()
380 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.'))
385 if res
== gtk
.RESPONSE_OK
:
386 m
= re
.match('^(http[s]?://|socket://)([\w.]+):(\d{1,5})$', url
)
387 res
= ['admin', 'admin']
389 res
.append( m
.group(2) )
390 res
.append( m
.group(3) )
391 res
.append( m
.group(1) )
394 self
.sig_login(dbname
=dbname
)
402 class terp_main(service
.Service
):
403 def __init__(self
, name
='gui.main', audience
='gui.*'):
404 service
.Service
.__init
__(self
, name
, audience
)
405 self
.exportMethod(self
.win_add
)
407 self
.glade
= glade
.XML(common
.terp_path("terp.glade"),"win_main",gettext
.textdomain())
408 self
.status_bar_main
= self
.glade
.get_widget('hbox_status_main')
409 self
.toolbar
= self
.glade
.get_widget('main_toolbar')
410 self
.sb_requests
= self
.glade
.get_widget('sb_requests')
411 self
.sb_username
= self
.glade
.get_widget('sb_user_name')
412 self
.sb_servername
= self
.glade
.get_widget('sb_user_server')
413 id = self
.sb_servername
.get_context_id('message')
414 self
.sb_servername
.push(id, _('Press Ctrl+O to login'))
415 self
.secure_img
= self
.glade
.get_widget('secure_img')
416 self
.secure_img
.hide()
418 window
= self
.glade
.get_widget('win_main')
419 window
.connect("destroy", self
.sig_quit
)
420 window
.connect("delete_event", self
.sig_delete
)
422 self
.window
.set_icon(common
.TINYERP_ICON
)
424 self
.notebook
= gtk
.Notebook()
425 self
.notebook
.popup_enable()
426 self
.notebook
.set_scrollable(True)
427 self
.sig_id
= self
.notebook
.connect_after('switch-page', self
._sig
_page
_changt
)
428 vbox
= self
.glade
.get_widget('vbox_main')
429 vbox
.pack_start(self
.notebook
, expand
=True, fill
=True)
431 self
.shortcut_menu
= self
.glade
.get_widget('shortcut')
434 # Code to add themes to the options->theme menu
437 menu
= self
.glade
.get_widget('menu_theme')
439 themes_path
= common
.terp_path('themes')
441 for dname
in os
.listdir(themes_path
):
442 if dname
.startswith('.'):
444 fname
= common
.terp_path(os
.path
.join('themes', dname
, 'gtkrc'))
445 if fname
and os
.path
.isfile(fname
):
446 open_item
= gtk
.RadioMenuItem(old
, dname
)
448 submenu
.append(open_item
)
449 if dname
== options
.options
['client.theme']:
450 open_item
.set_active(True)
451 open_item
.connect('toggled', self
.theme_select
, dname
)
453 submenu
.append(gtk
.SeparatorMenuItem())
454 open_item
= gtk
.RadioMenuItem(old
, _('Default Theme'))
455 submenu
.append(open_item
)
456 if 'none'==options
.options
['client.theme']:
457 open_item
.set_active(True)
458 open_item
.connect('toggled', self
.theme_select
, 'none')
460 menu
.set_submenu(submenu
)
468 self
.current_page
= 0
472 'on_login_activate': self
.sig_login
,
473 'on_logout_activate': self
.sig_logout
,
474 'on_win_next_activate': self
.sig_win_next
,
475 'on_win_prev_activate': self
.sig_win_prev
,
476 'on_plugin_execute_activate': self
.sig_plugin_execute
,
477 'on_quit_activate': self
.sig_close
,
478 'on_but_menu_clicked': self
.sig_win_menu
,
479 'on_win_new_activate': self
.sig_win_menu
,
480 'on_win_home_activate': self
.sig_home_new
,
481 'on_win_close_activate': self
.sig_win_close
,
482 'on_support_activate': common
.support
,
483 'on_preference_activate': self
.sig_user_preferences
,
484 'on_read_requests_activate': self
.sig_request_open
,
485 'on_send_request_activate': self
.sig_request_new
,
486 'on_request_wait_activate': self
.sig_request_wait
,
487 'on_opt_save_activate': lambda x
: options
.options
.save(),
488 'on_menubar_icons_activate': lambda x
: self
.sig_menubar('icons'),
489 'on_menubar_text_activate': lambda x
: self
.sig_menubar('text'),
490 'on_menubar_both_activate': lambda x
: self
.sig_menubar('both'),
491 'on_mode_normal_activate': lambda x
: self
.sig_mode_change(False),
492 'on_mode_pda_activate': lambda x
: self
.sig_mode_change(True),
493 'on_opt_form_tab_top_activate': lambda x
: self
.sig_form_tab('top'),
494 'on_opt_form_tab_left_activate': lambda x
: self
.sig_form_tab('left'),
495 'on_opt_form_tab_right_activate': lambda x
: self
.sig_form_tab('right'),
496 'on_opt_form_tab_bottom_activate': lambda x
: self
.sig_form_tab('bottom'),
497 'on_opt_form_tab_orientation_horizontal_activate': lambda x
: self
.sig_form_tab_orientation(0),
498 'on_opt_form_tab_orientation_vertical_activate': lambda x
: self
.sig_form_tab_orientation(90),
499 'on_help_index_activate': self
.sig_help_index
,
500 'on_help_contextual_activate': self
.sig_help_context
,
501 'on_help_tips_activate': self
.sig_tips
,
502 'on_help_licence_activate': self
.sig_licence
,
503 'on_about_activate': self
.sig_about
,
504 'on_shortcuts_activate' : self
.sig_shortcuts
,
505 'on_db_new_activate': self
.sig_db_new
,
506 'on_db_restore_activate': self
.sig_db_restore
,
507 'on_db_backup_activate': self
.sig_db_dump
,
508 'on_db_drop_activate': self
.sig_db_drop
,
509 'on_admin_password_activate': self
.sig_db_password
,
510 'on_extension_manager_activate': self
.sig_extension_manager
,
513 self
.glade
.signal_connect(signal
, dict[signal
])
516 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'):
517 self
.glade
.signal_connect('on_'+button
+'_clicked', self
._sig
_child
_call
, button
)
518 self
.buttons
[button
]=self
.glade
.get_widget(button
)
521 'form_del': 'but_remove',
522 'form_new': 'but_new',
523 'form_copy': 'but_copy',
524 'form_reload': 'but_reload',
525 'form_log': 'but_log',
526 'form_open': 'but_open',
527 'form_search': 'but_search',
528 'form_previous': 'but_previous',
529 'form_next': 'but_next',
530 'form_save': 'but_save',
531 'goto_id': 'but_goto_id',
532 'form_print': 'but_print',
533 'form_print_html': 'but_print_html',
534 'form_save_as': 'but_save_as',
535 'form_import': 'but_import',
536 'form_filter': 'but_filter',
537 'form_repeat': 'but_print_repeat'
540 self
.glade
.signal_connect('on_'+menu
+'_activate', self
._sig
_child
_call
, menus
[menu
])
542 spool
= service
.LocalService('spool')
543 spool
.subscribe('gui.window', self
.win_add
)
546 # we now create the icon for the attachment button when there are attachments
547 self
.__img
_no
_attachments
= gtk
.Image()
548 pxbf
= self
.window
.render_icon(self
.buttons
['but_attach'].get_stock_id(), self
.toolbar
.get_icon_size())
549 self
.__img
_no
_attachments
.set_from_pixbuf(pxbf
)
550 self
.__img
_no
_attachments
.show()
553 w
, h
= pxbf
.get_width(), pxbf
.get_height()
554 overlay
= self
.window
.render_icon(gtk
.STOCK_APPLY
, gtk
.ICON_SIZE_MENU
)
555 ow
, oh
= overlay
.get_width(), overlay
.get_height()
556 overlay
.composite(pxbf
,
561 gtk
.gdk
.INTERP_NEAREST
,
564 self
.__img
_attachments
= gtk
.Image()
565 self
.__img
_attachments
.set_from_pixbuf(pxbf
)
566 self
.__img
_attachments
.show()
570 settings
= gtk
.settings_get_default()
571 settings
.set_long_property('gtk-button-images', 1, 'TinyERP:gui.main')
573 def fnc_menuitem(menuitem
, opt_name
):
574 options
.options
[opt_name
] = menuitem
.get_active()
576 'on_opt_print_preview_activate': (fnc_menuitem
, 'printer.preview', 'opt_print_preview'),
577 'on_opt_form_toolbar_activate': (fnc_menuitem
, 'form.toolbar', 'opt_form_toolbar'),
579 self
.glade
.get_widget('menubar_'+(options
.options
['client.toolbar'] or 'both')).set_active(True)
580 self
.sig_menubar(options
.options
['client.toolbar'] or 'both')
581 self
.glade
.get_widget('opt_form_tab_'+(options
.options
['client.form_tab'] or 'left')).set_active(True)
582 self
.sig_form_tab(options
.options
['client.form_tab'] or 'left')
583 self
.glade
.get_widget('opt_form_tab_orientation_'+(str(options
.options
['client.form_tab_orientation']) or '0')).set_active(True)
584 self
.sig_form_tab_orientation(options
.options
['client.form_tab_orientation'] or 0)
585 if options
.options
['client.modepda']:
586 self
.glade
.get_widget('mode_pda').set_active(True)
588 self
.glade
.get_widget('mode_normal').set_active(True)
591 self
.glade
.signal_connect(signal
, dict[signal
][0], dict[signal
][1])
592 self
.glade
.get_widget(dict[signal
][2]).set_active(int(bool(options
.options
[dict[signal
][1]])))
594 # Adding a timer the check to requests
595 gobject
.timeout_add(5 * 60 * 1000, self
.request_set
)
598 def shortcut_edit(self
, widget
, model
='ir.ui.menu'):
599 obj
= service
.LocalService('gui.window')
600 domain
= [('user_id', '=', rpc
.session
.uid
), ('resource', '=', model
)]
601 obj
.create(False, 'ir.ui.view_sc', res_id
=None, domain
=domain
, view_type
='form', mode
='tree,form')
603 def shortcut_set(self
, sc
=None):
604 def _action_shortcut(widget
, action
):
606 ctx
= rpc
.session
.context
.copy()
607 obj
= service
.LocalService('action.main')
608 obj
.exec_keyword('tree_but_open', {'model': 'ir.ui.menu', 'id': action
[0],
609 'ids': [action
[0]], 'report_type': 'pdf', 'window': self
.window
}, context
=ctx
)
612 uid
= rpc
.session
.uid
613 sc
= rpc
.session
.rpc_exec_auth('/object', 'execute', 'ir.ui.view_sc', 'get_sc', uid
, 'ir.ui.menu', rpc
.session
.context
) or []
617 menuitem
= gtk
.MenuItem(s
['name'])
618 menuitem
.connect('activate', _action_shortcut
, s
['res_id'])
621 menu
.add(gtk
.SeparatorMenuItem())
622 menuitem
= gtk
.MenuItem(_('Edit'))
623 menuitem
.connect('activate', self
.shortcut_edit
)
627 self
.shortcut_menu
.set_submenu(menu
)
628 self
.shortcut_menu
.set_sensitive(True)
630 def shortcut_unset(self
):
633 self
.shortcut_menu
.set_submenu(menu
)
634 self
.shortcut_menu
.set_sensitive(False)
636 def theme_select(self
, widget
, theme
):
637 options
.options
['client.theme'] = theme
639 self
.window
.show_all()
642 def sig_mode_change(self
, pda_mode
=False):
643 options
.options
['client.modepda'] = pda_mode
644 return self
.sig_mode()
647 pda_mode
= options
.options
['client.modepda']
649 self
.status_bar_main
.hide()
651 self
.status_bar_main
.show()
654 def sig_menubar(self
, option
):
655 options
.options
['client.toolbar'] = option
657 self
.toolbar
.set_style(gtk
.TOOLBAR_BOTH
)
659 self
.toolbar
.set_style(gtk
.TOOLBAR_TEXT
)
660 elif option
=='icons':
661 self
.toolbar
.set_style(gtk
.TOOLBAR_ICONS
)
663 def sig_form_tab(self
, option
):
664 options
.options
['client.form_tab'] = option
666 def sig_form_tab_orientation(self
, option
):
667 options
.options
['client.form_tab_orientation'] = option
669 def sig_win_next(self
, args
):
670 pn
= self
.notebook
.get_current_page()
671 if pn
== len(self
.pages
)-1:
673 self
.notebook
.set_current_page(pn
+1)
675 def sig_win_prev(self
, args
):
676 pn
= self
.notebook
.get_current_page()
677 self
.notebook
.set_current_page(pn
-1)
679 def sig_user_preferences(self
, *args
):
680 win
=win_preference
.win_preference(parent
=self
.window
)
684 def sig_win_close(self
, *args
):
685 self
._sig
_child
_call
(args
[0], 'but_close')
687 def sig_request_new(self
, args
=None):
688 obj
= service
.LocalService('gui.window')
690 return obj
.create(None, 'res.request', False,
691 [('act_from', '=', rpc
.session
.uid
)], 'form',
692 mode
='form,tree', window
=self
.window
,
693 context
={'active_test': False})
697 def sig_request_open(self
, args
=None):
698 ids
,ids2
= self
.request_set()
699 obj
= service
.LocalService('gui.window')
701 return obj
.create(False, 'res.request', ids
,
702 [('act_to', '=', rpc
.session
.uid
), ('active', '=', True)],
703 'form', mode
='tree,form', window
=self
.window
,
704 context
={'active_test': False})
708 def sig_request_wait(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_from', '=', rpc
.session
.uid
),
714 ('state', '=', 'waiting'), ('active', '=', True)],
715 'form', mode
='tree,form', window
=self
.window
,
716 context
={'active_test': False})
720 def request_set(self
):
722 uid
= rpc
.session
.uid
723 ids
,ids2
= rpc
.session
.rpc_exec_auth_try('/object', 'execute',
724 'res.request', 'request_get')
726 message
= _('%s request(s)') % len(ids
)
728 message
= _('No request')
730 message
+= _(' - %s request(s) sended') % len(ids2
)
731 id = self
.sb_requests
.get_context_id('message')
732 self
.sb_requests
.push(id, message
)
737 def sig_login(self
, widget
=None, dbname
=False):
739 RES_BAD_PASSWORD
= -2
743 log_response
= RES_BAD_PASSWORD
745 while log_response
== RES_BAD_PASSWORD
:
748 res
= l
.run(dbname
=dbname
, parent
=self
.window
)
750 if e
.args
== ('QueryCanceled',):
753 service
.LocalService('gui.main').window
.present()
754 self
.sig_logout(widget
)
755 log_response
= rpc
.session
.login(*res
)
756 if log_response
== RES_OK
:
757 options
.options
.save()
758 id = self
.sig_win_menu(quiet
=False)
760 self
.sig_home_new(quiet
=True, except_id
=id)
761 if res
[4] == 'https://':
762 self
.secure_img
.show()
764 self
.secure_img
.hide()
766 elif log_response
== RES_CNX_ERROR
:
767 common
.message(_('Connection error !\nUnable to connect to the server !'))
768 elif log_response
== RES_BAD_PASSWORD
:
769 common
.message(_('Connection error !\nBad username or password !'))
770 except rpc
.rpc_exception
:
773 self
.glade
.get_widget('but_menu').set_sensitive(True)
774 self
.glade
.get_widget('user').set_sensitive(True)
775 self
.glade
.get_widget('form').set_sensitive(True)
776 self
.glade
.get_widget('plugins').set_sensitive(True)
779 def sig_logout(self
, widget
):
782 wid
= self
._wid
_get
()
784 if 'but_close' in wid
.handlers
:
785 res
= wid
.handlers
['but_close']()
788 res
= self
._win
_del
()
791 id = self
.sb_requests
.get_context_id('message')
792 self
.sb_requests
.push(id, '')
793 id = self
.sb_username
.get_context_id('message')
794 self
.sb_username
.push(id, _('Not logged !'))
795 id = self
.sb_servername
.get_context_id('message')
796 self
.sb_servername
.push(id, _('Press Ctrl+O to login'))
797 self
.secure_img
.hide()
798 self
.shortcut_unset()
799 self
.glade
.get_widget('but_menu').set_sensitive(False)
800 self
.glade
.get_widget('user').set_sensitive(False)
801 self
.glade
.get_widget('form').set_sensitive(False)
802 self
.glade
.get_widget('plugins').set_sensitive(False)
806 def sig_help_index(self
, widget
):
807 tools
.launch_browser(options
.options
['help.index'])
809 def sig_help_context(self
, widget
):
810 model
= self
._wid
_get
().model
811 l
= rpc
.session
.context
.get('lang','en_US')
812 tools
.launch_browser(options
.options
['help.context']+'?model=%s&lang=%s' % (model
,l
))
814 def sig_tips(self
, *args
):
815 common
.tipoftheday(self
.window
)
817 def sig_licence(self
, widget
):
818 dialog
= glade
.XML(common
.terp_path("terp.glade"), "win_licence", gettext
.textdomain())
819 dialog
.signal_connect("on_but_ok_pressed", lambda obj
: dialog
.get_widget('win_licence').destroy())
821 win
= dialog
.get_widget('win_licence')
822 win
.set_transient_for(self
.window
)
825 def sig_about(self
, widget
):
826 about
= glade
.XML(common
.terp_path("terp.glade"), "win_about", gettext
.textdomain())
827 buffer = about
.get_widget('textview2').get_buffer()
828 about_txt
= buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter())
829 buffer.set_text(about_txt
% tinyerp_version
)
830 about
.signal_connect("on_but_ok_pressed", lambda obj
: about
.get_widget('win_about').destroy())
832 win
= about
.get_widget('win_about')
833 win
.set_transient_for(self
.window
)
836 def sig_shortcuts(self
, widget
):
837 shortcuts_win
= glade
.XML(common
.terp_path('terp.glade'), 'shortcuts_dia', gettext
.textdomain())
838 shortcuts_win
.signal_connect("on_but_ok_pressed", lambda obj
: shortcuts_win
.get_widget('shortcuts_dia').destroy())
840 win
= shortcuts_win
.get_widget('shortcuts_dia')
841 win
.set_transient_for(self
.window
)
844 def sig_win_menu(self
, widget
=None, quiet
=True):
845 for p
in range(len(self
.pages
)):
846 if self
.pages
[p
].model
=='ir.ui.menu':
847 self
.notebook
.set_current_page(p
)
849 res
= self
.sig_win_new(widget
, type='menu_id', quiet
=quiet
)
851 return self
.sig_win_new(widget
, type='action_id', quiet
=quiet
)
854 def sig_win_new(self
, widget
=None, type='menu_id', quiet
=True, except_id
=False):
856 act_id
= rpc
.session
.rpc_exec_auth('/object', 'execute', 'res.users',
857 'read', [rpc
.session
.uid
], [type,'name'], rpc
.session
.context
)
860 id = self
.sb_username
.get_context_id('message')
861 self
.sb_username
.push(id, act_id
[0]['name'] or '')
862 id = self
.sb_servername
.get_context_id('message')
863 data
= urlparse
.urlsplit(rpc
.session
._url
)
864 self
.sb_servername
.push(id, data
[0]+':'+(data
[1] and '//'+data
[1] \
865 or data
[2])+' ['+options
.options
['login.db']+']')
866 if not act_id
[0][type]:
869 common
.warning(_("You can not log into the system !\nAsk the administrator to verify\nyou have an action defined for your user."),'Access Denied !')
872 act_id
= act_id
[0][type][0]
873 if except_id
and act_id
== except_id
:
875 obj
= service
.LocalService('action.main')
876 win
= obj
.execute(act_id
, {'window':self
.window
})
878 user
= rpc
.session
.rpc_exec_auth_wo('/object', 'execute', 'res.users',
879 'read', [rpc
.session
.uid
], [type,'name'], rpc
.session
.context
)
881 act_id
= user
[0][type][0]
886 def sig_home_new(self
, widget
=None, quiet
=True, except_id
=False):
887 return self
.sig_win_new(widget
, type='action_id', quiet
=quiet
,
890 def sig_plugin_execute(self
, widget
):
892 pn
= self
.notebook
.get_current_page()
893 datas
= {'model': self
.pages
[pn
].model
, 'ids':self
.pages
[pn
].ids_get(), 'id' : self
.pages
[pn
].id_get()}
894 plugins
.execute(datas
)
896 def sig_quit(self
, widget
):
897 options
.options
.save()
900 def sig_close(self
, widget
):
901 if common
.sur(_("Do you really want to quit ?"), parent
=self
.window
):
902 if not self
.sig_logout(widget
):
904 options
.options
.save()
907 def sig_delete(self
, widget
, event
, data
=None):
908 if common
.sur(_("Do you really want to quit ?"), parent
=self
.window
):
909 if not self
.sig_logout(widget
):
914 def win_add(self
, win
, datas
):
915 self
.pages
.append(win
)
916 self
.notebook
.append_page(win
.widget
, gtk
.Label(win
.name
))
917 self
.notebook
.set_current_page(-1)
919 def message(self
, message
):
920 id = self
.status_bar
.get_context_id('message')
921 self
.status_bar
.push(id, message
)
923 def __attachment_callback(self
, view
, objid
):
924 current_view
= self
._wid
_get
()
925 current_id
= current_view
and current_view
.id_get()
926 if current_view
== view
and objid
== current_id
:
928 if objid
and view
.screen
.current_view
.view_type
== 'form':
929 cpt
= rpc
.session
.rpc_exec_auth('/object', 'execute',
930 'ir.attachment', 'search_count',
931 [('res_model', '=', view
.model
), ('res_id', '=', objid
)])
933 self
.buttons
['but_attach'].set_icon_widget(self
.__img
_attachments
)
934 self
.buttons
['but_attach'].set_label(_('Attachments (%d)') % cpt
)
937 def _update_attachment_button(self
, view
= None):
939 Update the attachment icon for display the number of attachments
942 view
= self
._wid
_get
()
944 id = view
and view
.id_get()
945 gobject
.timeout_add(1500, self
.__attachment
_callback
, view
, id)
946 self
.buttons
['but_attach'].set_icon_widget(self
.__img
_no
_attachments
)
947 self
.buttons
['but_attach'].set_label(_('Attachments'))
950 def sb_set(self
, view
=None):
952 view
= self
._wid
_get
()
953 self
._update
_attachment
_button
(view
)
954 for x
in self
.buttons
:
956 self
.buttons
[x
].set_sensitive(view
and (x
in view
.handlers
))
959 pn
= self
.notebook
.get_current_page()
961 self
.notebook
.disconnect(self
.sig_id
)
962 page
= self
.pages
.pop(pn
)
963 self
.notebook
.remove_page(pn
)
964 self
.sig_id
= self
.notebook
.connect_after('switch-page', self
._sig
_page
_changt
)
969 return self
.notebook
.get_current_page() != -1
972 pn
= self
.notebook
.get_current_page()
975 return self
.pages
[pn
]
977 def _sig_child_call(self
, widget
, button_name
, *args
):
978 wid
= self
._wid
_get
()
981 if button_name
in wid
.handlers
:
982 res
= wid
.handlers
[button_name
]()
983 # for those buttons, we refresh the attachment button.
984 # for the "switch view" button, the action has already
985 # been called by the Screen object of the view (wid)
986 if button_name
in ('but_new', 'but_remove', 'but_search', \
987 'but_previous', 'but_next', 'but_open', \
988 'but_close', 'but_reload', 'but_attach', 'but_goto_id'):
989 self
._update
_attachment
_button
(wid
)
990 if button_name
=='but_close' and res
:
993 def _sig_page_changt(self
, widget
=None, *args
):
994 self
.last_page
= self
.current_page
995 self
.current_page
= self
.notebook
.get_current_page()
998 def sig_db_new(self
, widget
):
999 if not self
.sig_logout(widget
):
1001 dia
= db_create(self
.sig_login
)
1002 res
= dia
.run(self
.window
)
1004 options
.options
.save()
1007 def sig_db_drop(self
, widget
):
1008 if not self
.sig_logout(widget
):
1010 url
, db_name
, passwd
= self
._choose
_db
_select
(_('Delete a database'))
1015 rpc
.session
.db_exec(url
, 'drop', passwd
, db_name
)
1016 common
.message(_("Database dropped successfully !"), parent
=self
.window
)
1017 except Exception, e
:
1018 if ('faultString' in e
and e
.faultString
=='AccessDenied:None') or str(e
)=='AccessDenied':
1019 common
.warning(_('Bad database administrator password !'),_("Could not drop database."), parent
=self
.window
)
1021 common
.warning(_("Couldn't drop database"), parent
=self
.window
)
1023 def sig_db_restore(self
, widget
):
1024 filename
= common
.file_selection(_('Open...'), parent
=self
.window
, preview
=False)
1028 url
, db_name
, passwd
= self
._choose
_db
_ent
()
1031 f
= file(filename
, 'rb')
1032 data_b64
= base64
.encodestring(f
.read())
1034 rpc
.session
.db_exec(url
, 'restore', passwd
, db_name
, data_b64
)
1035 common
.message(_("Database restored successfully !"), parent
=self
.window
)
1037 if ('faultString' in e
and e
.faultString
=='AccessDenied:None') or str(e
)=='AccessDenied':
1038 common
.warning(_('Bad database administrator password !'),_("Could not restore database."), parent
=self
.window
)
1040 common
.warning(_("Couldn't restore database"), parent
=self
.window
)
1042 def sig_extension_manager(self
,widget
):
1043 win
= win_extension
.win_extension(self
.window
)
1046 def sig_db_password(self
, widget
):
1047 dialog
= glade
.XML(common
.terp_path("terp.glade"), "dia_passwd_change",
1048 gettext
.textdomain())
1049 win
= dialog
.get_widget('dia_passwd_change')
1050 win
.set_icon(common
.TINYERP_ICON
)
1051 win
.set_transient_for(self
.window
)
1053 server_widget
= dialog
.get_widget('ent_server')
1054 old_pass_widget
= dialog
.get_widget('old_passwd')
1055 new_pass_widget
= dialog
.get_widget('new_passwd')
1056 new_pass2_widget
= dialog
.get_widget('new_passwd2')
1057 change_button
= dialog
.get_widget('but_server_change')
1058 change_button
.connect_after('clicked', lambda a
,b
: _server_ask(b
, win
), server_widget
)
1060 host
= options
.options
['login.server']
1061 port
= options
.options
['login.port']
1062 protocol
= options
.options
['login.protocol']
1063 url
= '%s%s:%s' % (protocol
, host
, port
)
1064 server_widget
.set_text(url
)
1067 if res
== gtk
.RESPONSE_OK
:
1068 url
= server_widget
.get_text()
1069 old_passwd
= old_pass_widget
.get_text()
1070 new_passwd
= new_pass_widget
.get_text()
1071 new_passwd2
= new_pass2_widget
.get_text()
1072 if new_passwd
!= new_passwd2
:
1073 common
.warning(_("Confirmation password do not match " \
1074 "new password, operation cancelled!"),
1075 _("Validation Error."), parent
=win
)
1078 rpc
.session
.db_exec(url
, 'change_admin_password',
1079 old_passwd
, new_passwd
)
1081 if ('faultString' in e
and e
.faultString
=='AccessDenied:None') \
1082 or str(e
)=='AccessDenied':
1083 common
.warning(_("Could not change password database."),
1084 _('Bas password provided !'), parent
=win
)
1086 common
.warning(_("Error, password not changed."),
1088 self
.window
.present()
1091 def sig_db_dump(self
, widget
):
1092 url
, db_name
, passwd
= self
._choose
_db
_select
(_('Backup a database'))
1095 filename
= common
.file_selection(_('Save As...'),
1096 action
=gtk
.FILE_CHOOSER_ACTION_SAVE
, parent
=self
.window
, preview
=False)
1100 dump_b64
= rpc
.session
.db_exec(url
, 'dump', passwd
, db_name
)
1101 dump
= base64
.decodestring(dump_b64
)
1102 f
= file(filename
, 'wb')
1105 common
.message(_("Database backuped successfully !"), parent
=self
.window
)
1107 common
.warning(_("Couldn't backup database."), parent
=self
.window
)
1109 def _choose_db_select(self
, title
=_("Backup a database")):
1110 def refreshlist(widget
, db_widget
, label
, url
):
1111 res
= _refresh_dblist(db_widget
, url
)
1113 label
.set_label('<b>'+_('Could not connect to server !')+'</b>')
1117 label
.set_label('<b>'+_('No database found, you must create one !')+'</b>')
1125 def refreshlist_ask(widget
, server_widget
, db_widget
, label
, parent
=None):
1126 url
= _server_ask(server_widget
, parent
)
1129 refreshlist(widget
, db_widget
, label
, url
)
1132 dialog
= glade
.XML(common
.terp_path("terp.glade"), "win_db_select",
1133 gettext
.textdomain())
1134 win
= dialog
.get_widget('win_db_select')
1135 win
.set_icon(common
.TINYERP_ICON
)
1136 win
.set_default_response(gtk
.RESPONSE_OK
)
1137 win
.set_transient_for(self
.window
)
1140 pass_widget
= dialog
.get_widget('ent_passwd_select')
1141 server_widget
= dialog
.get_widget('ent_server_select')
1142 db_widget
= dialog
.get_widget('combo_db_select')
1143 label
= dialog
.get_widget('label_db_select')
1146 dialog
.get_widget('db_select_label').set_markup('<b>'+title
+'</b>')
1148 protocol
= options
.options
['login.protocol']
1149 url
= '%s%s:%s' % (protocol
, options
.options
['login.server'], options
.options
['login.port'])
1150 server_widget
.set_text(url
)
1152 liststore
= gtk
.ListStore(str)
1153 db_widget
.set_model(liststore
)
1155 refreshlist(None, db_widget
, label
, url
)
1156 change_button
= dialog
.get_widget('but_server_select')
1157 change_button
.connect_after('clicked', refreshlist_ask
, server_widget
, db_widget
, label
, win
)
1159 cell
= gtk
.CellRendererText()
1160 db_widget
.pack_start(cell
, True)
1161 db_widget
.add_attribute(cell
, 'text', 0)
1168 if res
== gtk
.RESPONSE_OK
:
1169 db
= db_widget
.get_active_text()
1170 url
= server_widget
.get_text()
1171 passwd
= pass_widget
.get_text()
1172 self
.window
.present()
1174 return (url
,db
,passwd
)
1176 def _choose_db_ent(self
):
1177 dialog
= glade
.XML(common
.terp_path("terp.glade"), "win_db_ent",
1178 gettext
.textdomain())
1179 win
= dialog
.get_widget('win_db_ent')
1180 win
.set_icon(common
.TINYERP_ICON
)
1181 win
.set_transient_for(self
.window
)
1184 db_widget
= dialog
.get_widget('ent_db')
1185 widget_pass
= dialog
.get_widget('ent_password')
1186 widget_url
= dialog
.get_widget('ent_server')
1188 protocol
= options
.options
['login.protocol']
1189 url
= '%s%s:%s' % (protocol
, options
.options
['login.server'],
1190 options
.options
['login.port'])
1191 widget_url
.set_text(url
)
1193 change_button
= dialog
.get_widget('but_server_change')
1194 change_button
.connect_after('clicked', lambda a
,b
: _server_ask(b
, win
),
1202 if res
== gtk
.RESPONSE_OK
:
1203 db
= db_widget
.get_text()
1204 url
= widget_url
.get_text()
1205 passwd
= widget_pass
.get_text()
1206 self
.window
.present()
1208 return url
, db
, passwd
1211 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: