1 ##############################################################################
3 # Copyright (c) 2004-2008 TINY SPRL. (http://tiny.be) All Rights Reserved.
7 # WARNING: This program as such is intended to be used by professional
8 # programmers who take the whole responsability of assessing all potential
9 # consequences resulting from its eventual inadequacies and bugs
10 # End users who are looking for a ready-to-use solution with commercial
11 # garantees and support are strongly adviced to contract a Free Software
14 # This program is Free Software; you can redistribute it and/or
15 # modify it under the terms of the GNU General Public License
16 # as published by the Free Software Foundation; either version 2
17 # of the License, or (at your option) any later version.
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
24 # You should have received a copy of the GNU General Public License
25 # along with this program; if not, write to the Free Software
26 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
28 ##############################################################################
45 from window
import win_preference
51 def _refresh_dblist(db_widget
, url
, dbtoload
=None):
53 dbtoload
= options
.options
['login.db']
55 liststore
= db_widget
.get_model()
57 result
= rpc
.session
.list_db(url
)
60 for db_num
, db_name
in enumerate(rpc
.session
.list_db(url
)):
61 liststore
.append([db_name
])
62 if db_name
== dbtoload
:
64 db_widget
.set_active(index
)
67 def _refresh_langlist(lang_widget
, url
):
68 liststore
= lang_widget
.get_model()
70 lang_list
= rpc
.session
.db_exec_no_except(url
, 'list_lang')
71 lang_list
.append( ('en_US','English') )
72 for key
,val
in lang_list
:
73 liststore
.insert(0, (val
,key
))
74 lang_widget
.set_active(0)
77 def _server_ask(server_widget
, parent
=None):
79 win_gl
= glade
.XML(common
.terp_path("terp.glade"),"win_server",gettext
.textdomain())
80 win
= win_gl
.get_widget('win_server')
82 parent
= service
.LocalService('gui.main').window
83 win
.set_transient_for(parent
)
84 win
.set_icon(common
.TINYERP_ICON
)
86 win
.set_default_response(gtk
.RESPONSE_OK
)
87 host_widget
= win_gl
.get_widget('ent_host')
88 port_widget
= win_gl
.get_widget('ent_port')
89 protocol_widget
= win_gl
.get_widget('protocol')
91 protocol
={'XML-RPC': 'http://',
92 'XML-RPC secure': 'https://',
93 'NET-RPC (faster)': 'socket://',}
94 listprotocol
= gtk
.ListStore(str)
95 protocol_widget
.set_model(listprotocol
)
98 m
= re
.match('^(http[s]?://|socket://)([\w.-]+):(\d{1,5})$', server_widget
.get_text())
100 host_widget
.set_text(m
.group(2))
101 port_widget
.set_text(m
.group(3))
106 listprotocol
.append([p
])
107 if m
and protocol
[p
] == m
.group(1):
110 protocol_widget
.set_active(index
)
113 if res
== gtk
.RESPONSE_OK
:
114 protocol
= protocol
[protocol_widget
.get_active_text()]
115 url
= '%s%s:%s' % (protocol
, host_widget
.get_text(), port_widget
.get_text())
116 server_widget
.set_text(url
)
123 class db_login(object):
125 self
.win_gl
= glade
.XML(common
.terp_path("terp.glade"),"win_login",gettext
.textdomain())
127 def refreshlist(self
, widget
, db_widget
, label
, url
, butconnect
=False):
128 res
= _refresh_dblist(db_widget
, url
)
130 label
.set_label('<b>'+_('Could not connect to server !')+'</b>')
134 butconnect
.set_sensitive(False)
136 label
.set_label('<b>'+_('No database found, you must create one !')+'</b>')
140 butconnect
.set_sensitive(False)
145 butconnect
.set_sensitive(True)
148 def refreshlist_ask(self
,widget
, server_widget
, db_widget
, label
, butconnect
= False, url
=False, parent
=None):
149 url
= _server_ask(server_widget
, parent
) or url
150 return self
.refreshlist(widget
, db_widget
, label
, url
, butconnect
)
152 def run(self
, dbname
=None, parent
=None):
154 win
= self
.win_gl
.get_widget('win_login')
156 parent
= service
.LocalService('gui.main').window
157 win
.set_transient_for(parent
)
158 win
.set_icon(common
.TINYERP_ICON
)
160 img
= self
.win_gl
.get_widget('image_tinyerp')
161 img
.set_from_file(common
.terp_path_pixmaps('tinyerp.png'))
162 login
= self
.win_gl
.get_widget('ent_login')
163 passwd
= self
.win_gl
.get_widget('ent_passwd')
164 server_widget
= self
.win_gl
.get_widget('ent_server')
165 but_connect
= self
.win_gl
.get_widget('button_connect')
166 db_widget
= self
.win_gl
.get_widget('combo_db')
167 change_button
= self
.win_gl
.get_widget('but_server')
168 label
= self
.win_gl
.get_widget('combo_label')
171 host
= options
.options
['login.server']
172 port
= options
.options
['login.port']
173 protocol
= options
.options
['login.protocol']
175 url
= '%s%s:%s' % (protocol
, host
, port
)
176 server_widget
.set_text(url
)
177 login
.set_text(options
.options
['login.login'])
179 # construct the list of available db and select the last one used
180 liststore
= gtk
.ListStore(str)
181 db_widget
.set_model(liststore
)
182 cell
= gtk
.CellRendererText()
183 db_widget
.pack_start(cell
, True)
184 db_widget
.add_attribute(cell
, 'text', 0)
186 res
= self
.refreshlist(None, db_widget
, label
, url
, but_connect
)
187 change_button
.connect_after('clicked', self
.refreshlist_ask
, server_widget
, db_widget
, label
, but_connect
, url
, win
)
190 iter = liststore
.get_iter_root()
192 if liststore
.get_value(iter, 0)==dbname
:
193 db_widget
.set_active_iter(iter)
195 iter = liststore
.iter_next(iter)
198 m
= re
.match('^(http[s]?://|socket://)([\w.\-]+):(\d{1,5})$', server_widget
.get_text() or '')
200 options
.options
['login.server'] = m
.group(2)
201 options
.options
['login.login'] = login
.get_text()
202 options
.options
['login.port'] = m
.group(3)
203 options
.options
['login.protocol'] = m
.group(1)
204 options
.options
['login.db'] = db_widget
.get_active_text()
205 result
= (login
.get_text(), passwd
.get_text(), m
.group(2), m
.group(3), m
.group(1), db_widget
.get_active_text())
209 raise Exception('QueryCanceled')
210 if res
<> gtk
.RESPONSE_OK
:
213 raise Exception('QueryCanceled')
218 class db_create(object):
219 def set_sensitive(self
, sensitive
):
221 label
= self
.dialog
.get_widget('db_label_info')
222 label
.set_text(_('Do not use special characters !'))
223 self
.dialog
.get_widget('button_db_ok').set_sensitive(True)
225 label
= self
.dialog
.get_widget('db_label_info')
226 label
.set_markup('<b>'+_('Can not connect to server, please change it !')+'</b>')
227 self
.dialog
.get_widget('button_db_ok').set_sensitive(False)
230 def server_change(self
, widget
=None, parent
=None):
231 url
= _server_ask(self
.server_widget
)
233 if self
.lang_widget
and url
:
234 _refresh_langlist(self
.lang_widget
, url
)
235 self
.set_sensitive(True)
237 self
.set_sensitive(False)
241 def __init__(self
, sig_login
):
242 self
.dialog
= glade
.XML(common
.terp_path("terp.glade"), "win_createdb", gettext
.textdomain())
243 self
.sig_login
= sig_login
245 def run(self
, parent
=None):
246 win
= self
.dialog
.get_widget('win_createdb')
247 win
.set_default_response(gtk
.RESPONSE_OK
)
249 parent
= service
.LocalService('gui.main').window
250 win
.set_transient_for(parent
)
253 pass_widget
= self
.dialog
.get_widget('ent_password_new')
254 self
.server_widget
= self
.dialog
.get_widget('ent_server_new')
255 change_button
= self
.dialog
.get_widget('but_server_new')
256 self
.lang_widget
= self
.dialog
.get_widget('db_create_combo')
257 self
.db_widget
= self
.dialog
.get_widget('ent_db_new')
258 demo_widget
= self
.dialog
.get_widget('check_demo')
259 demo_widget
.set_active(True)
261 change_button
.connect_after('clicked', self
.server_change
, win
)
262 protocol
= options
.options
['login.protocol']
263 url
= '%s%s:%s' % (protocol
, options
.options
['login.server'], options
.options
['login.port'])
265 self
.server_widget
.set_text(url
)
266 liststore
= gtk
.ListStore(str, str)
267 self
.lang_widget
.set_model(liststore
)
269 _refresh_langlist(self
.lang_widget
, url
)
271 self
.set_sensitive(False)
275 db_name
= self
.db_widget
.get_text()
276 if (res
==gtk
.RESPONSE_OK
) and ((not db_name
) or (not re
.match('^[a-zA-Z][a-zA-Z0-9_]+$', db_name
))):
277 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
)
281 demo_data
= demo_widget
.get_active()
283 langidx
= self
.lang_widget
.get_active_iter()
284 langreal
= langidx
and self
.lang_widget
.get_model().get_value(langidx
,1)
285 passwd
= pass_widget
.get_text()
286 url
= self
.server_widget
.get_text()
287 m
= re
.match('^(http[s]?://|socket://)([\w.\-]+):(\d{1,5})$', url
or '')
289 options
.options
['login.server'] = m
.group(2)
290 options
.options
['login.port'] = m
.group(3)
291 options
.options
['login.protocol'] = m
.group(1)
295 if res
== gtk
.RESPONSE_OK
:
297 id=rpc
.session
.db_exec(url
, 'list')
299 raise Exception('DbExist')
300 id = rpc
.session
.db_exec(url
, 'create', passwd
, db_name
, demo_data
, langreal
)
301 win
= gtk
.Window(type=gtk
.WINDOW_TOPLEVEL
)
302 win
.set_position(gtk
.WIN_POS_CENTER_ON_PARENT
)
303 vbox
= gtk
.VBox(False, 0)
304 hbox
= gtk
.HBox(False, 13)
305 hbox
.set_border_width(10)
307 img
.set_from_stock('gtk-dialog-info', gtk
.ICON_SIZE_DIALOG
)
308 hbox
.pack_start(img
, expand
=True, fill
=False)
309 vbox2
= gtk
.VBox(False, 0)
311 label
.set_markup(_('<b>Operation in progress</b>'))
312 label
.set_alignment(0.0, 0.5)
313 vbox2
.pack_start(label
, expand
=True, fill
=False)
314 vbox2
.pack_start(gtk
.HSeparator(), expand
=True, fill
=True)
315 vbox2
.pack_start(gtk
.Label(_("Please wait,\nthis operation may take a while...")), expand
=True, fill
=False)
316 hbox
.pack_start(vbox2
, expand
=True, fill
=True)
317 vbox
.pack_start(hbox
)
318 pb
= gtk
.ProgressBar()
319 pb
.set_orientation(gtk
.PROGRESS_LEFT_TO_RIGHT
)
320 vbox
.pack_start(pb
, expand
=True, fill
=False)
323 parent
= service
.LocalService('gui.main').window
324 win
.set_transient_for(parent
)
326 self
.timer
= gobject
.timeout_add(1000, self
.progress_timeout
, pb
, url
, passwd
, id, win
, db_name
, parent
)
328 if e
.args
== ('DbExist',):
329 common
.warning(_("Could not create database."),_('Database alreayd exists !'))
330 elif ('faultString' in e
and e
.faultString
=='AccessDenied:None') or str(e
)=='AccessDenied':
331 common
.warning(_('Bad database administrator password !'), _("Could not create database."))
337 common
.warning(_("Could not create database."),_('Error during database creation !'))
339 def progress_timeout(self
, pbar
, url
, passwd
, id, win
, dbname
, parent
=None):
341 progress
,users
= rpc
.session
.db_exec_no_except(url
, 'get_progress', passwd
, id)
344 common
.warning(_("The server crashed during installation.\nWe suggest you to drop this database."),_("Error during database creation !"))
351 pwdlst
= '\n'.join(map(lambda x
: ' - %s: %s / %s' % (x
['name'],x
['login'],x
['password']), users
))
352 dialog
= glade
.XML(common
.terp_path("terp.glade"), "dia_dbcreate_ok", gettext
.textdomain())
353 win
= dialog
.get_widget('dia_dbcreate_ok')
355 parent
= service
.LocalService('gui.main').window
356 win
.set_transient_for(parent
)
358 buffer = dialog
.get_widget('dia_tv').get_buffer()
360 buffer.delete(buffer.get_start_iter(), buffer.get_end_iter())
361 iter_start
= buffer.get_start_iter()
362 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.'))
367 if res
== gtk
.RESPONSE_OK
:
368 m
= re
.match('^(http[s]?://|socket://)([\w.]+):(\d{1,5})$', url
)
369 res
= ['admin', 'admin']
371 res
.append( m
.group(2) )
372 res
.append( m
.group(3) )
373 res
.append( m
.group(1) )
376 self
.sig_login(dbname
=dbname
)
384 class terp_main(service
.Service
):
385 def __init__(self
, name
='gui.main', audience
='gui.*'):
386 service
.Service
.__init
__(self
, name
, audience
)
387 self
.exportMethod(self
.win_add
)
389 self
.glade
= glade
.XML(common
.terp_path("terp.glade"),"win_main",gettext
.textdomain())
390 self
.status_bar_main
= self
.glade
.get_widget('hbox_status_main')
391 self
.toolbar
= self
.glade
.get_widget('main_toolbar')
392 self
.sb_requests
= self
.glade
.get_widget('sb_requests')
393 self
.sb_username
= self
.glade
.get_widget('sb_user_name')
394 self
.sb_servername
= self
.glade
.get_widget('sb_user_server')
395 id = self
.sb_servername
.get_context_id('message')
396 self
.sb_servername
.push(id, _('Press Ctrl+O to login'))
397 self
.secure_img
= self
.glade
.get_widget('secure_img')
398 self
.secure_img
.hide()
400 window
= self
.glade
.get_widget('win_main')
401 window
.connect("destroy", self
.sig_quit
)
402 window
.connect("delete_event", self
.sig_delete
)
404 self
.window
.set_icon(common
.TINYERP_ICON
)
406 self
.notebook
= gtk
.Notebook()
407 self
.notebook
.popup_enable()
408 self
.notebook
.set_scrollable(True)
409 self
.sig_id
= self
.notebook
.connect_after('switch-page', self
._sig
_page
_changt
)
410 vbox
= self
.glade
.get_widget('vbox_main')
411 vbox
.pack_start(self
.notebook
, expand
=True, fill
=True)
413 self
.shortcut_menu
= self
.glade
.get_widget('shortcut')
416 # Code to add themes to the options->theme menu
419 menu
= self
.glade
.get_widget('menu_theme')
421 themes_path
= common
.terp_path('themes')
423 for dname
in os
.listdir(themes_path
):
424 if dname
.startswith('.'):
426 fname
= common
.terp_path(os
.path
.join('themes', dname
, 'gtkrc'))
427 if fname
and os
.path
.isfile(fname
):
428 open_item
= gtk
.RadioMenuItem(old
, dname
)
430 submenu
.append(open_item
)
431 if dname
== options
.options
['client.theme']:
432 open_item
.set_active(True)
433 open_item
.connect('toggled', self
.theme_select
, dname
)
435 submenu
.append(gtk
.SeparatorMenuItem())
436 open_item
= gtk
.RadioMenuItem(old
, _('Default Theme'))
437 submenu
.append(open_item
)
438 if 'none'==options
.options
['client.theme']:
439 open_item
.set_active(True)
440 open_item
.connect('toggled', self
.theme_select
, 'none')
442 menu
.set_submenu(submenu
)
450 self
.current_page
= 0
454 'on_login_activate': self
.sig_login
,
455 'on_logout_activate': self
.sig_logout
,
456 'on_win_next_activate': self
.sig_win_next
,
457 'on_win_prev_activate': self
.sig_win_prev
,
458 'on_plugin_execute_activate': self
.sig_plugin_execute
,
459 'on_quit_activate': self
.sig_close
,
460 'on_but_menu_clicked': self
.sig_win_menu
,
461 'on_win_new_activate': self
.sig_win_menu
,
462 'on_win_home_activate': self
.sig_home_new
,
463 'on_win_close_activate': self
.sig_win_close
,
464 'on_support_activate': common
.support
,
465 'on_preference_activate': self
.sig_user_preferences
,
466 'on_read_requests_activate': self
.sig_request_open
,
467 'on_send_request_activate': self
.sig_request_new
,
468 'on_request_wait_activate': self
.sig_request_wait
,
469 'on_opt_save_activate': lambda x
: options
.options
.save(),
470 'on_menubar_icons_activate': lambda x
: self
.sig_menubar('icons'),
471 'on_menubar_text_activate': lambda x
: self
.sig_menubar('text'),
472 'on_menubar_both_activate': lambda x
: self
.sig_menubar('both'),
473 'on_mode_normal_activate': lambda x
: self
.sig_mode_change(False),
474 'on_mode_pda_activate': lambda x
: self
.sig_mode_change(True),
475 'on_opt_form_tab_top_activate': lambda x
: self
.sig_form_tab('top'),
476 'on_opt_form_tab_left_activate': lambda x
: self
.sig_form_tab('left'),
477 'on_opt_form_tab_right_activate': lambda x
: self
.sig_form_tab('right'),
478 'on_opt_form_tab_bottom_activate': lambda x
: self
.sig_form_tab('bottom'),
479 'on_opt_form_tab_orientation_horizontal_activate': lambda x
: self
.sig_form_tab_orientation(0),
480 'on_opt_form_tab_orientation_vertical_activate': lambda x
: self
.sig_form_tab_orientation(90),
481 'on_help_index_activate': self
.sig_help_index
,
482 'on_help_contextual_activate': self
.sig_help_context
,
483 'on_help_tips_activate': self
.sig_tips
,
484 'on_help_licence_activate': self
.sig_licence
,
485 'on_about_activate': self
.sig_about
,
486 'on_shortcuts_activate' : self
.sig_shortcuts
,
487 'on_db_new_activate': self
.sig_db_new
,
488 'on_db_restore_activate': self
.sig_db_restore
,
489 'on_db_backup_activate': self
.sig_db_dump
,
490 'on_db_drop_activate': self
.sig_db_drop
,
491 'on_admin_password_activate': self
.sig_db_password
,
494 self
.glade
.signal_connect(signal
, dict[signal
])
497 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'):
498 self
.glade
.signal_connect('on_'+button
+'_clicked', self
._sig
_child
_call
, button
)
499 self
.buttons
[button
]=self
.glade
.get_widget(button
)
502 'form_del': 'but_remove',
503 'form_new': 'but_new',
504 'form_copy': 'but_copy',
505 'form_reload': 'but_reload',
506 'form_log': 'but_log',
507 'form_open': 'but_open',
508 'form_search': 'but_search',
509 'form_previous': 'but_previous',
510 'form_next': 'but_next',
511 'form_save': 'but_save',
512 'goto_id': 'but_goto_id',
513 'form_print': 'but_print',
514 'form_print_html': 'but_print_html',
515 'form_save_as': 'but_save_as',
516 'form_import': 'but_import',
517 'form_filter': 'but_filter',
518 'form_repeat': 'but_print_repeat'
521 self
.glade
.signal_connect('on_'+menu
+'_activate', self
._sig
_child
_call
, menus
[menu
])
523 spool
= service
.LocalService('spool')
524 spool
.subscribe('gui.window', self
.win_add
)
527 # we now create the icon for the attachment button when there are attachments
528 self
.__img
_no
_attachments
= gtk
.Image()
529 pxbf
= self
.window
.render_icon(self
.buttons
['but_attach'].get_stock_id(), self
.toolbar
.get_icon_size())
530 self
.__img
_no
_attachments
.set_from_pixbuf(pxbf
)
531 self
.__img
_no
_attachments
.show()
534 w
, h
= pxbf
.get_width(), pxbf
.get_height()
535 overlay
= self
.window
.render_icon(gtk
.STOCK_APPLY
, gtk
.ICON_SIZE_MENU
)
536 ow
, oh
= overlay
.get_width(), overlay
.get_height()
537 overlay
.composite(pxbf
,
542 gtk
.gdk
.INTERP_NEAREST
,
545 self
.__img
_attachments
= gtk
.Image()
546 self
.__img
_attachments
.set_from_pixbuf(pxbf
)
547 self
.__img
_attachments
.show()
551 settings
= gtk
.settings_get_default()
552 settings
.set_long_property('gtk-button-images', 1, 'TinyERP:gui.main')
554 def fnc_menuitem(menuitem
, opt_name
):
555 options
.options
[opt_name
] = menuitem
.get_active()
557 'on_opt_print_preview_activate': (fnc_menuitem
, 'printer.preview', 'opt_print_preview'),
558 'on_opt_form_toolbar_activate': (fnc_menuitem
, 'form.toolbar', 'opt_form_toolbar'),
560 self
.glade
.get_widget('menubar_'+(options
.options
['client.toolbar'] or 'both')).set_active(True)
561 self
.sig_menubar(options
.options
['client.toolbar'] or 'both')
562 self
.glade
.get_widget('opt_form_tab_'+(options
.options
['client.form_tab'] or 'left')).set_active(True)
563 self
.sig_form_tab(options
.options
['client.form_tab'] or 'left')
564 self
.glade
.get_widget('opt_form_tab_orientation_'+(str(options
.options
['client.form_tab_orientation']) or '0')).set_active(True)
565 self
.sig_form_tab_orientation(options
.options
['client.form_tab_orientation'] or 0)
566 if options
.options
['client.modepda']:
567 self
.glade
.get_widget('mode_pda').set_active(True)
569 self
.glade
.get_widget('mode_normal').set_active(True)
572 self
.glade
.signal_connect(signal
, dict[signal
][0], dict[signal
][1])
573 self
.glade
.get_widget(dict[signal
][2]).set_active(int(bool(options
.options
[dict[signal
][1]])))
575 # Adding a timer the check to requests
576 gobject
.timeout_add(5 * 60 * 1000, self
.request_set
)
579 def shortcut_edit(self
, widget
, model
='ir.ui.menu'):
580 obj
= service
.LocalService('gui.window')
581 domain
= [('user_id', '=', rpc
.session
.uid
), ('resource', '=', model
)]
582 obj
.create(False, 'ir.ui.view_sc', res_id
=None, domain
=domain
, view_type
='form', mode
='tree,form')
584 def shortcut_set(self
, sc
=None):
585 def _action_shortcut(widget
, action
):
587 ctx
= rpc
.session
.context
.copy()
588 obj
= service
.LocalService('action.main')
589 obj
.exec_keyword('tree_but_open', {'model': 'ir.ui.menu', 'id': action
,
590 'ids': [action
], 'report_type': 'pdf', 'window': self
.window
}, context
=ctx
)
593 uid
= rpc
.session
.uid
594 sc
= rpc
.session
.rpc_exec_auth('/object', 'execute', 'ir.ui.view_sc', 'get_sc', uid
, 'ir.ui.menu', rpc
.session
.context
) or []
598 menuitem
= gtk
.MenuItem(s
['name'])
599 menuitem
.connect('activate', _action_shortcut
, s
['res_id'])
602 menu
.add(gtk
.SeparatorMenuItem())
603 menuitem
= gtk
.MenuItem(_('Edit'))
604 menuitem
.connect('activate', self
.shortcut_edit
)
608 self
.shortcut_menu
.set_submenu(menu
)
609 self
.shortcut_menu
.set_sensitive(True)
611 def shortcut_unset(self
):
614 self
.shortcut_menu
.set_submenu(menu
)
615 self
.shortcut_menu
.set_sensitive(False)
617 def theme_select(self
, widget
, theme
):
618 options
.options
['client.theme'] = theme
620 self
.window
.show_all()
623 def sig_mode_change(self
, pda_mode
=False):
624 options
.options
['client.modepda'] = pda_mode
625 return self
.sig_mode()
628 pda_mode
= options
.options
['client.modepda']
630 self
.status_bar_main
.hide()
632 self
.status_bar_main
.show()
635 def sig_menubar(self
, option
):
636 options
.options
['client.toolbar'] = option
638 self
.toolbar
.set_style(gtk
.TOOLBAR_BOTH
)
640 self
.toolbar
.set_style(gtk
.TOOLBAR_TEXT
)
641 elif option
=='icons':
642 self
.toolbar
.set_style(gtk
.TOOLBAR_ICONS
)
644 def sig_form_tab(self
, option
):
645 options
.options
['client.form_tab'] = option
647 def sig_form_tab_orientation(self
, option
):
648 options
.options
['client.form_tab_orientation'] = option
650 def sig_win_next(self
, args
):
651 pn
= self
.notebook
.get_current_page()
652 if pn
== len(self
.pages
)-1:
654 self
.notebook
.set_current_page(pn
+1)
656 def sig_win_prev(self
, args
):
657 pn
= self
.notebook
.get_current_page()
658 self
.notebook
.set_current_page(pn
-1)
660 def sig_user_preferences(self
, *args
):
661 win
=win_preference
.win_preference(parent
=self
.window
)
665 def sig_win_close(self
, *args
):
666 self
._sig
_child
_call
(args
[0], 'but_close')
668 def sig_request_new(self
, args
=None):
669 obj
= service
.LocalService('gui.window')
671 return obj
.create(None, 'res.request', False,
672 [('act_from', '=', rpc
.session
.uid
)], 'form',
673 mode
='form,tree', window
=self
.window
,
674 context
={'active_test': False})
678 def sig_request_open(self
, args
=None):
679 ids
,ids2
= self
.request_set()
680 obj
= service
.LocalService('gui.window')
682 return obj
.create(False, 'res.request', ids
,
683 [('act_to', '=', rpc
.session
.uid
), ('active', '=', True)],
684 'form', mode
='tree,form', window
=self
.window
,
685 context
={'active_test': False})
689 def sig_request_wait(self
, args
=None):
690 ids
,ids2
= self
.request_set()
691 obj
= service
.LocalService('gui.window')
693 return obj
.create(False, 'res.request', ids
,
694 [('act_from', '=', rpc
.session
.uid
),
695 ('state', '=', 'waiting'), ('active', '=', True)],
696 'form', mode
='tree,form', window
=self
.window
,
697 context
={'active_test': False})
701 def request_set(self
):
703 uid
= rpc
.session
.uid
704 ids
,ids2
= rpc
.session
.rpc_exec_auth_try('/object', 'execute',
705 'res.request', 'request_get')
707 message
= _('%s request(s)') % len(ids
)
709 message
= _('No request')
711 message
+= _(' - %s request(s) sended') % len(ids2
)
712 id = self
.sb_requests
.get_context_id('message')
713 self
.sb_requests
.push(id, message
)
718 def sig_login(self
, widget
=None, dbname
=False):
720 RES_BAD_PASSWORD
= -2
724 log_response
= RES_BAD_PASSWORD
726 while log_response
== RES_BAD_PASSWORD
:
729 res
= l
.run(dbname
=dbname
, parent
=self
.window
)
731 if e
.args
== ('QueryCanceled',):
734 service
.LocalService('gui.main').window
.present()
735 self
.sig_logout(widget
)
736 log_response
= rpc
.session
.login(*res
)
737 if log_response
== RES_OK
:
738 options
.options
.save()
739 id = self
.sig_win_menu(quiet
=False)
741 self
.sig_home_new(quiet
=True, except_id
=id)
742 if res
[4] == 'https://':
743 self
.secure_img
.show()
745 self
.secure_img
.hide()
747 elif log_response
== RES_CNX_ERROR
:
748 common
.message(_('Connection error !\nUnable to connect to the server !'))
749 elif log_response
== RES_BAD_PASSWORD
:
750 common
.message(_('Connection error !\nBad username or password !'))
751 except rpc
.rpc_exception
:
754 self
.glade
.get_widget('but_menu').set_sensitive(True)
755 self
.glade
.get_widget('user').set_sensitive(True)
756 self
.glade
.get_widget('form').set_sensitive(True)
757 self
.glade
.get_widget('plugins').set_sensitive(True)
760 def sig_logout(self
, widget
):
763 wid
= self
._wid
_get
()
765 if 'but_close' in wid
.handlers
:
766 res
= wid
.handlers
['but_close']()
769 res
= self
._win
_del
()
772 id = self
.sb_requests
.get_context_id('message')
773 self
.sb_requests
.push(id, '')
774 id = self
.sb_username
.get_context_id('message')
775 self
.sb_username
.push(id, _('Not logged !'))
776 id = self
.sb_servername
.get_context_id('message')
777 self
.sb_servername
.push(id, _('Press Ctrl+O to login'))
778 self
.secure_img
.hide()
779 self
.shortcut_unset()
780 self
.glade
.get_widget('but_menu').set_sensitive(False)
781 self
.glade
.get_widget('user').set_sensitive(False)
782 self
.glade
.get_widget('form').set_sensitive(False)
783 self
.glade
.get_widget('plugins').set_sensitive(False)
787 def sig_help_index(self
, widget
):
788 tools
.launch_browser('http://www.openerp.com/documentation/user-manual/')
790 def sig_help_context(self
, widget
):
791 model
= self
._wid
_get
().model
792 l
= rpc
.session
.context
.get('lang','en_US')
793 tools
.launch_browser('http://www.topenerp.com/scripts/context_index.php?model=%s&lang=%s' % (model
,l
))
795 def sig_tips(self
, *args
):
796 common
.tipoftheday(self
.window
)
798 def sig_licence(self
, widget
):
799 dialog
= glade
.XML(common
.terp_path("terp.glade"), "win_licence", gettext
.textdomain())
800 dialog
.signal_connect("on_but_ok_pressed", lambda obj
: dialog
.get_widget('win_licence').destroy())
802 win
= dialog
.get_widget('win_licence')
803 win
.set_transient_for(self
.window
)
806 def sig_about(self
, widget
):
807 about
= glade
.XML(common
.terp_path("terp.glade"), "win_about", gettext
.textdomain())
808 buffer = about
.get_widget('textview2').get_buffer()
809 about_txt
= buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter())
810 buffer.set_text(about_txt
% tinyerp_version
)
811 about
.signal_connect("on_but_ok_pressed", lambda obj
: about
.get_widget('win_about').destroy())
813 win
= about
.get_widget('win_about')
814 win
.set_transient_for(self
.window
)
817 def sig_shortcuts(self
, widget
):
818 shortcuts_win
= glade
.XML(common
.terp_path('terp.glade'), 'shortcuts_dia', gettext
.textdomain())
819 shortcuts_win
.signal_connect("on_but_ok_pressed", lambda obj
: shortcuts_win
.get_widget('shortcuts_dia').destroy())
821 win
= shortcuts_win
.get_widget('shortcuts_dia')
822 win
.set_transient_for(self
.window
)
825 def sig_win_menu(self
, widget
=None, quiet
=True):
826 for p
in range(len(self
.pages
)):
827 if self
.pages
[p
].model
=='ir.ui.menu':
828 self
.notebook
.set_current_page(p
)
830 res
= self
.sig_win_new(widget
, type='menu_id', quiet
=quiet
)
832 return self
.sig_win_new(widget
, type='action_id', quiet
=quiet
)
835 def sig_win_new(self
, widget
=None, type='menu_id', quiet
=True, except_id
=False):
837 act_id
= rpc
.session
.rpc_exec_auth('/object', 'execute', 'res.users',
838 'read', [rpc
.session
.uid
], [type,'name'], rpc
.session
.context
)
841 id = self
.sb_username
.get_context_id('message')
842 self
.sb_username
.push(id, act_id
[0]['name'] or '')
843 id = self
.sb_servername
.get_context_id('message')
844 data
= urlparse
.urlsplit(rpc
.session
._url
)
845 self
.sb_servername
.push(id, data
[0]+':'+(data
[1] and '//'+data
[1] \
846 or data
[2])+' ['+options
.options
['login.db']+']')
847 if not act_id
[0][type]:
850 common
.warning(_("You can not log into the system !\nAsk the administrator to verify\nyou have an action defined for your user."),'Access Denied !')
853 act_id
= act_id
[0][type][0]
854 if except_id
and act_id
== except_id
:
856 obj
= service
.LocalService('action.main')
857 win
= obj
.execute(act_id
, {'window':self
.window
})
859 user
= rpc
.session
.rpc_exec_auth_wo('/object', 'execute', 'res.users',
860 'read', [rpc
.session
.uid
], [type,'name'], rpc
.session
.context
)
862 act_id
= user
[0][type][0]
867 def sig_home_new(self
, widget
=None, quiet
=True, except_id
=False):
868 return self
.sig_win_new(widget
, type='action_id', quiet
=quiet
,
871 def sig_plugin_execute(self
, widget
):
873 pn
= self
.notebook
.get_current_page()
874 datas
= {'model': self
.pages
[pn
].model
, 'ids':self
.pages
[pn
].ids_get(), 'id' : self
.pages
[pn
].id_get()}
875 plugins
.execute(datas
)
877 def sig_quit(self
, widget
):
878 options
.options
.save()
881 def sig_close(self
, widget
):
882 if common
.sur(_("Do you really want to quit ?"), parent
=self
.window
):
883 if not self
.sig_logout(widget
):
885 options
.options
.save()
888 def sig_delete(self
, widget
, event
, data
=None):
889 if common
.sur(_("Do you really want to quit ?"), parent
=self
.window
):
890 if not self
.sig_logout(widget
):
895 def win_add(self
, win
, datas
):
896 self
.pages
.append(win
)
897 self
.notebook
.append_page(win
.widget
, gtk
.Label(win
.name
))
898 self
.notebook
.set_current_page(-1)
900 def message(self
, message
):
901 id = self
.status_bar
.get_context_id('message')
902 self
.status_bar
.push(id, message
)
904 def __attachment_callback(self
, view
, objid
):
905 current_view
= self
._wid
_get
()
906 current_id
= current_view
and current_view
.id_get()
907 if current_view
== view
and objid
== current_id
:
909 if objid
and view
.screen
.current_view
.view_type
== 'form':
910 cpt
= rpc
.session
.rpc_exec_auth('/object', 'execute',
911 'ir.attachment', 'search_count',
912 [('res_model', '=', view
.model
), ('res_id', '=', objid
)])
914 self
.buttons
['but_attach'].set_icon_widget(self
.__img
_attachments
)
915 self
.buttons
['but_attach'].set_label(_('Attachments (%d)') % cpt
)
918 def _update_attachment_button(self
, view
= None):
920 Update the attachment icon for display the number of attachments
923 view
= self
._wid
_get
()
925 id = view
and view
.id_get()
926 gobject
.timeout_add(1500, self
.__attachment
_callback
, view
, id)
927 self
.buttons
['but_attach'].set_icon_widget(self
.__img
_no
_attachments
)
928 self
.buttons
['but_attach'].set_label(_('Attachments'))
931 def sb_set(self
, view
=None):
933 view
= self
._wid
_get
()
934 self
._update
_attachment
_button
(view
)
935 for x
in self
.buttons
:
937 self
.buttons
[x
].set_sensitive(view
and (x
in view
.handlers
))
940 pn
= self
.notebook
.get_current_page()
942 self
.notebook
.disconnect(self
.sig_id
)
943 page
= self
.pages
.pop(pn
)
944 self
.notebook
.remove_page(pn
)
945 self
.sig_id
= self
.notebook
.connect_after('switch-page', self
._sig
_page
_changt
)
950 return self
.notebook
.get_current_page() != -1
953 pn
= self
.notebook
.get_current_page()
956 return self
.pages
[pn
]
958 def _sig_child_call(self
, widget
, button_name
, *args
):
959 wid
= self
._wid
_get
()
962 if button_name
in wid
.handlers
:
963 res
= wid
.handlers
[button_name
]()
964 # for those buttons, we refresh the attachment button.
965 # for the "switch view" button, the action has already
966 # been called by the Screen object of the view (wid)
967 if button_name
in ('but_new', 'but_remove', 'but_search', \
968 'but_previous', 'but_next', 'but_open', \
969 'but_close', 'but_reload', 'but_attach', 'but_goto_id'):
970 self
._update
_attachment
_button
(wid
)
971 if button_name
=='but_close' and res
:
974 def _sig_page_changt(self
, widget
=None, *args
):
975 self
.last_page
= self
.current_page
976 self
.current_page
= self
.notebook
.get_current_page()
979 def sig_db_new(self
, widget
):
980 if not self
.sig_logout(widget
):
982 dia
= db_create(self
.sig_login
)
983 res
= dia
.run(self
.window
)
985 options
.options
.save()
988 def sig_db_drop(self
, widget
):
989 if not self
.sig_logout(widget
):
991 url
, db_name
, passwd
= self
._choose
_db
_select
(_('Delete a database'))
996 rpc
.session
.db_exec(url
, 'drop', passwd
, db_name
)
997 common
.message(_("Database dropped successfully !"), parent
=self
.window
)
999 if ('faultString' in e
and e
.faultString
=='AccessDenied:None') or str(e
)=='AccessDenied':
1000 common
.warning(_('Bad database administrator password !'),_("Could not drop database."), parent
=self
.window
)
1002 common
.warning(_("Couldn't drop database"), parent
=self
.window
)
1004 def sig_db_restore(self
, widget
):
1005 filename
= common
.file_selection(_('Open...'), parent
=self
.window
, preview
=False)
1009 url
, db_name
, passwd
= self
._choose
_db
_ent
()
1012 f
= file(filename
, 'rb')
1013 data_b64
= base64
.encodestring(f
.read())
1015 rpc
.session
.db_exec(url
, 'restore', passwd
, db_name
, data_b64
)
1016 common
.message(_("Database restored successfully !"), parent
=self
.window
)
1018 if ('faultString' in e
and e
.faultString
=='AccessDenied:None') or str(e
)=='AccessDenied':
1019 common
.warning(_('Bad database administrator password !'),_("Could not restore database."), parent
=self
.window
)
1021 common
.warning(_("Couldn't restore database"), parent
=self
.window
)
1023 def sig_db_password(self
, widget
):
1024 dialog
= glade
.XML(common
.terp_path("terp.glade"), "dia_passwd_change",
1025 gettext
.textdomain())
1026 win
= dialog
.get_widget('dia_passwd_change')
1027 win
.set_icon(common
.TINYERP_ICON
)
1028 win
.set_transient_for(self
.window
)
1030 server_widget
= dialog
.get_widget('ent_server')
1031 old_pass_widget
= dialog
.get_widget('old_passwd')
1032 new_pass_widget
= dialog
.get_widget('new_passwd')
1033 new_pass2_widget
= dialog
.get_widget('new_passwd2')
1034 change_button
= dialog
.get_widget('but_server_change')
1035 change_button
.connect_after('clicked', lambda a
,b
: _server_ask(b
, win
), server_widget
)
1037 host
= options
.options
['login.server']
1038 port
= options
.options
['login.port']
1039 protocol
= options
.options
['login.protocol']
1040 url
= '%s%s:%s' % (protocol
, host
, port
)
1041 server_widget
.set_text(url
)
1044 if res
== gtk
.RESPONSE_OK
:
1045 url
= server_widget
.get_text()
1046 old_passwd
= old_pass_widget
.get_text()
1047 new_passwd
= new_pass_widget
.get_text()
1048 new_passwd2
= new_pass2_widget
.get_text()
1049 if new_passwd
!= new_passwd2
:
1050 common
.warning(_("Confirmation password do not match " \
1051 "new password, operation cancelled!"),
1052 _("Validation Error."), parent
=win
)
1055 rpc
.session
.db_exec(url
, 'change_admin_password',
1056 old_passwd
, new_passwd
)
1058 if ('faultString' in e
and e
.faultString
=='AccessDenied:None') \
1059 or str(e
)=='AccessDenied':
1060 common
.warning(_("Could not change password database."),
1061 _('Bas password provided !'), parent
=win
)
1063 common
.warning(_("Error, password not changed."),
1065 self
.window
.present()
1068 def sig_db_dump(self
, widget
):
1069 url
, db_name
, passwd
= self
._choose
_db
_select
(_('Backup a database'))
1072 filename
= common
.file_selection(_('Save As...'),
1073 action
=gtk
.FILE_CHOOSER_ACTION_SAVE
, parent
=self
.window
, preview
=False)
1077 dump_b64
= rpc
.session
.db_exec(url
, 'dump', passwd
, db_name
)
1078 dump
= base64
.decodestring(dump_b64
)
1079 f
= file(filename
, 'wb')
1082 common
.message(_("Database backuped successfully !"), parent
=self
.window
)
1084 common
.warning(_("Couldn't backup database."), parent
=self
.window
)
1086 def _choose_db_select(self
, title
=_("Backup a database")):
1087 def refreshlist(widget
, db_widget
, label
, url
):
1088 res
= _refresh_dblist(db_widget
, url
)
1090 label
.set_label('<b>'+_('Could not connect to server !')+'</b>')
1094 label
.set_label('<b>'+_('No database found, you must create one !')+'</b>')
1102 def refreshlist_ask(widget
, server_widget
, db_widget
, label
, parent
=None):
1103 url
= _server_ask(server_widget
, parent
)
1106 refreshlist(widget
, db_widget
, label
, url
)
1109 dialog
= glade
.XML(common
.terp_path("terp.glade"), "win_db_select",
1110 gettext
.textdomain())
1111 win
= dialog
.get_widget('win_db_select')
1112 win
.set_icon(common
.TINYERP_ICON
)
1113 win
.set_default_response(gtk
.RESPONSE_OK
)
1114 win
.set_transient_for(self
.window
)
1117 pass_widget
= dialog
.get_widget('ent_passwd_select')
1118 server_widget
= dialog
.get_widget('ent_server_select')
1119 db_widget
= dialog
.get_widget('combo_db_select')
1120 label
= dialog
.get_widget('label_db_select')
1123 dialog
.get_widget('db_select_label').set_markup('<b>'+title
+'</b>')
1125 protocol
= options
.options
['login.protocol']
1126 url
= '%s%s:%s' % (protocol
, options
.options
['login.server'], options
.options
['login.port'])
1127 server_widget
.set_text(url
)
1129 liststore
= gtk
.ListStore(str)
1130 db_widget
.set_model(liststore
)
1132 refreshlist(None, db_widget
, label
, url
)
1133 change_button
= dialog
.get_widget('but_server_select')
1134 change_button
.connect_after('clicked', refreshlist_ask
, server_widget
, db_widget
, label
, win
)
1136 cell
= gtk
.CellRendererText()
1137 db_widget
.pack_start(cell
, True)
1138 db_widget
.add_attribute(cell
, 'text', 0)
1145 if res
== gtk
.RESPONSE_OK
:
1146 db
= db_widget
.get_active_text()
1147 url
= server_widget
.get_text()
1148 passwd
= pass_widget
.get_text()
1149 self
.window
.present()
1151 return (url
,db
,passwd
)
1153 def _choose_db_ent(self
):
1154 dialog
= glade
.XML(common
.terp_path("terp.glade"), "win_db_ent",
1155 gettext
.textdomain())
1156 win
= dialog
.get_widget('win_db_ent')
1157 win
.set_icon(common
.TINYERP_ICON
)
1158 win
.set_transient_for(self
.window
)
1161 db_widget
= dialog
.get_widget('ent_db')
1162 widget_pass
= dialog
.get_widget('ent_password')
1163 widget_url
= dialog
.get_widget('ent_server')
1165 protocol
= options
.options
['login.protocol']
1166 url
= '%s%s:%s' % (protocol
, options
.options
['login.server'],
1167 options
.options
['login.port'])
1168 widget_url
.set_text(url
)
1170 change_button
= dialog
.get_widget('but_server_change')
1171 change_button
.connect_after('clicked', lambda a
,b
: _server_ask(b
, win
),
1179 if res
== gtk
.RESPONSE_OK
:
1180 db
= db_widget
.get_text()
1181 url
= widget_url
.get_text()
1182 passwd
= widget_pass
.get_text()
1183 self
.window
.present()
1185 return url
, db
, passwd