Remove the unused widget
[openerp-client.git] / bin / modules / gui / main.py
blob7cfbf58eb45020a5ab4899601a88ff90f590f8fa
1 # -*- encoding: utf-8 -*-
2 ##############################################################################
4 # Copyright (c) 2004-2008 TINY SPRL. (http://tiny.be) All Rights Reserved.
6 # $Id$
8 # WARNING: This program as such is intended to be used by professional
9 # programmers who take the whole responsability of assessing all potential
10 # consequences resulting from its eventual inadequacies and bugs
11 # End users who are looking for a ready-to-use solution with commercial
12 # garantees and support are strongly adviced to contract a Free Software
13 # Service Company
15 # This program is Free Software; you can redistribute it and/or
16 # modify it under the terms of the GNU General Public License
17 # as published by the Free Software Foundation; either version 2
18 # of the License, or (at your option) any later version.
20 # This program is distributed in the hope that it will be useful,
21 # but WITHOUT ANY WARRANTY; without even the implied warranty of
22 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 # GNU General Public License for more details.
25 # You should have received a copy of the GNU General Public License
26 # along with this program; if not, write to the Free Software
27 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 ##############################################################################
31 import time
32 import os
33 import gettext
34 import urlparse
36 import gobject
37 import gtk
38 from gtk import glade
39 import translate
41 import rpc
43 import service
44 import options
45 import common
47 from window import win_preference, win_extension
48 import tools
49 import re
50 import xmlrpclib
51 import base64
53 def _refresh_dblist(db_widget, url, dbtoload=None):
54 if not dbtoload:
55 dbtoload = options.options['login.db']
56 index = 0
57 liststore = db_widget.get_model()
58 liststore.clear()
59 result = rpc.session.list_db(url)
60 if result==-1:
61 return -1
62 for db_num, db_name in enumerate(rpc.session.list_db(url)):
63 liststore.append([db_name])
64 if db_name == dbtoload:
65 index = db_num
66 db_widget.set_active(index)
67 return len(liststore)
69 def _refresh_langlist(lang_widget, url):
70 liststore = lang_widget.get_model()
71 liststore.clear()
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)
77 return lang_list
79 def _server_ask(server_widget, parent=None):
80 result = False
81 win_gl = glade.XML(common.terp_path("terp.glade"),"win_server",gettext.textdomain())
82 win = win_gl.get_widget('win_server')
83 if not parent:
84 parent = service.LocalService('gui.main').window
85 win.set_transient_for(parent)
86 win.set_icon(common.TINYERP_ICON)
87 win.show_all()
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())
101 if m:
102 host_widget.set_text(m.group(2))
103 port_widget.set_text(m.group(3))
105 index = 0
106 i = 0
107 for p in protocol:
108 listprotocol.append([p])
109 if m and protocol[p] == m.group(1):
110 index = i
111 i += 1
112 protocol_widget.set_active(index)
114 res = win.run()
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)
119 result = url
120 parent.present()
121 win.destroy()
122 return result
125 class db_login(object):
126 def __init__(self):
127 self.win_gl = glade.XML(common.terp_path("terp.glade"),"win_login",gettext.textdomain())
129 def refreshlist(self, widget, db_widget, label, url, butconnect=False):
131 def check_server_version(url):
132 try:
133 import release
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)
138 except:
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)
143 if res == -1:
144 label.set_label('<b>'+_('Could not connect to server !')+'</b>')
145 db_widget.hide()
146 label.show()
147 if butconnect:
148 butconnect.set_sensitive(False)
149 else:
150 if res==0:
151 label.set_label('<b>'+_('No database found, you must create one !')+'</b>')
152 db_widget.hide()
153 label.show()
154 if butconnect:
155 butconnect.set_sensitive(False)
156 else:
157 label.hide()
158 db_widget.show()
159 if butconnect:
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,))
165 return res
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):
172 uid = 0
173 win = self.win_gl.get_widget('win_login')
174 if not parent:
175 parent = service.LocalService('gui.main').window
176 win.set_transient_for(parent)
177 win.set_icon(common.TINYERP_ICON)
178 win.show_all()
179 img = self.win_gl.get_widget('image_tinyerp')
180 img.set_from_file(common.terp_path_pixmaps('tinyerp.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')
188 label.hide()
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)
208 if dbname:
209 iter = liststore.get_iter_root()
210 while iter:
211 if liststore.get_value(iter, 0)==dbname:
212 db_widget.set_active_iter(iter)
213 break
214 iter = liststore.iter_next(iter)
216 res = win.run()
217 m = re.match('^(http[s]?://|socket://)([\w.\-]+):(\d{1,5})$', server_widget.get_text() or '')
218 if m:
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())
225 else:
226 parent.present()
227 win.destroy()
228 raise Exception('QueryCanceled')
229 if res <> gtk.RESPONSE_OK:
230 parent.present()
231 win.destroy()
232 raise Exception('QueryCanceled')
233 parent.present()
234 win.destroy()
235 return result
237 class db_create(object):
238 def set_sensitive(self, sensitive):
239 self.dialog.get_widget('button_db_ok').set_sensitive(False)
240 return sensitive
242 def server_change(self, widget=None, parent=None):
243 url = _server_ask(self.server_widget)
244 try:
245 if self.lang_widget and url:
246 _refresh_langlist(self.lang_widget, url)
247 self.set_sensitive(True)
248 except:
249 self.set_sensitive(False)
250 return False
251 return url
253 def __init__(self, sig_login, terp_main):
254 self.dialog = glade.XML(common.terp_path("terp.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)
268 if not parent:
269 parent = service.LocalService('gui.main').window
270 win.set_transient_for(parent)
271 win.show_all()
272 lang_dict = {}
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)
288 try:
289 _refresh_langlist(self.lang_widget, url)
290 except:
291 self.set_sensitive(False)
293 while True:
294 res = win.run()
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)
298 continue
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)
302 else:
303 break
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 '')
312 if m:
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 parent.present()
317 win.destroy()
319 if res == gtk.RESPONSE_OK:
320 try:
321 id=rpc.session.db_exec(url, 'list')
322 if db_name in id:
323 raise Exception('DbExist')
324 id = rpc.session.db_exec(url, 'create', passwd, db_name, demo_data, langreal, user_pass)
325 win = gtk.Window(type=gtk.WINDOW_TOPLEVEL)
326 win.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
327 win.set_title(_('OpenERP Database Installation'))
328 vbox = gtk.VBox(False, 0)
329 hbox = gtk.HBox(False, 13)
330 hbox.set_border_width(10)
331 img = gtk.Image()
332 img.set_from_stock('gtk-dialog-info', gtk.ICON_SIZE_DIALOG)
333 hbox.pack_start(img, expand=True, fill=False)
334 vbox2 = gtk.VBox(False, 0)
335 label = gtk.Label()
336 label.set_markup(_('<b>Operation in progress</b>'))
337 label.set_alignment(0.0, 0.5)
338 vbox2.pack_start(label, expand=True, fill=False)
339 vbox2.pack_start(gtk.HSeparator(), expand=True, fill=True)
340 vbox2.pack_start(gtk.Label(_("Please wait,\nthis operation may take a while...")), expand=True, fill=False)
341 hbox.pack_start(vbox2, expand=True, fill=True)
342 vbox.pack_start(hbox)
343 pb = gtk.ProgressBar()
344 pb.set_orientation(gtk.PROGRESS_LEFT_TO_RIGHT)
345 vbox.pack_start(pb, expand=True, fill=False)
346 win.add(vbox)
347 if not parent:
348 parent = service.LocalService('gui.main').window
349 win.set_transient_for(parent)
350 win.show_all()
351 self.timer = gobject.timeout_add(1000, self.progress_timeout, pb, url, passwd, id, win, db_name, parent)
352 except Exception, e:
353 if e.args == ('DbExist',):
354 common.warning(_("Could not create database."),_('Database already exists !'))
355 elif ('faultString' in e and e.faultString=='AccessDenied:None') or str(e)=='AccessDenied':
356 common.warning(_('Bad database administrator password !'), _("Could not create database."))
357 else:
358 common.warning(_("Could not create database."),_('Error during database creation !'))
360 def progress_timeout(self, pbar, url, passwd, id, win, dbname, parent=None):
361 try:
362 progress,users = rpc.session.db_exec_no_except(url, 'get_progress', passwd, id)
363 except:
364 win.destroy()
365 common.warning(_("The server crashed during installation.\nWe suggest you to drop this database."),_("Error during database creation !"))
366 return False
368 pbar.pulse()
369 if progress == 1.0:
370 win.destroy()
372 pwdlst = '\n'.join(map(lambda x: ' - %s: %s / %s' % (x['name'],x['login'],x['password']), users))
373 dialog = glade.XML(common.terp_path("terp.glade"), "dia_dbcreate_ok", gettext.textdomain())
374 win = dialog.get_widget('dia_dbcreate_ok')
375 if not parent:
376 parent = service.LocalService('gui.main').window
377 win.set_transient_for(parent)
378 win.show_all()
379 buffer = dialog.get_widget('dia_tv').get_buffer()
381 buffer.delete(buffer.get_start_iter(), buffer.get_end_iter())
382 iter_start = buffer.get_start_iter()
383 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.'))
384 res = win.run()
385 parent.present()
386 win.destroy()
388 if res == gtk.RESPONSE_OK:
389 m = re.match('^(http[s]?://|socket://)([\w.]+):(\d{1,5})$', url)
390 ok = False
391 for x in users:
392 if x['login']=='admin' and m:
393 res = [x['login'], x['password']]
394 res.append( m.group(2) )
395 res.append( m.group(3) )
396 res.append( m.group(1) )
397 res.append( dbname )
398 log_response = rpc.session.login(*res)
399 if log_response == 1:
400 id = self.terp_main.sig_win_menu(quiet=False)
401 ok = True
402 if not ok:
403 self.sig_login(dbname=dbname)
404 return False
405 return True
407 def process(self):
408 return False
411 class terp_main(service.Service):
412 def __init__(self, name='gui.main', audience='gui.*'):
413 service.Service.__init__(self, name, audience)
414 self.exportMethod(self.win_add)
416 self._handler_ok = True
417 self.glade = glade.XML(common.terp_path("terp.glade"),"win_main",gettext.textdomain())
418 self.status_bar_main = self.glade.get_widget('hbox_status_main')
419 self.toolbar = self.glade.get_widget('main_toolbar')
420 self.sb_requests = self.glade.get_widget('sb_requests')
421 self.sb_username = self.glade.get_widget('sb_user_name')
422 self.sb_servername = self.glade.get_widget('sb_user_server')
423 id = self.sb_servername.get_context_id('message')
424 self.sb_servername.push(id, _('Press Ctrl+O to login'))
425 self.secure_img = self.glade.get_widget('secure_img')
426 self.secure_img.hide()
428 window = self.glade.get_widget('win_main')
429 window.connect("destroy", self.sig_quit)
430 window.connect("delete_event", self.sig_delete)
431 self.window = window
432 self.window.set_icon(common.TINYERP_ICON)
434 self.notebook = gtk.Notebook()
435 self.notebook.popup_enable()
436 self.notebook.set_scrollable(True)
437 self.sig_id = self.notebook.connect_after('switch-page', self._sig_page_changt)
438 vbox = self.glade.get_widget('vbox_main')
439 vbox.pack_start(self.notebook, expand=True, fill=True)
441 self.shortcut_menu = self.glade.get_widget('shortcut')
444 # Code to add themes to the options->theme menu
446 submenu = gtk.Menu()
447 menu = self.glade.get_widget('menu_theme')
448 old = None
449 themes_path = common.terp_path('themes')
450 if themes_path:
451 for dname in os.listdir(themes_path):
452 if dname.startswith('.'):
453 continue
454 fname = common.terp_path(os.path.join('themes', dname, 'gtkrc'))
455 if fname and os.path.isfile(fname):
456 open_item = gtk.RadioMenuItem(old, dname)
457 old = open_item
458 submenu.append(open_item)
459 if dname == options.options['client.theme']:
460 open_item.set_active(True)
461 open_item.connect('toggled', self.theme_select, dname)
463 submenu.append(gtk.SeparatorMenuItem())
464 open_item = gtk.RadioMenuItem(old, _('Default Theme'))
465 submenu.append(open_item)
466 if 'none'==options.options['client.theme']:
467 open_item.set_active(True)
468 open_item.connect('toggled', self.theme_select, 'none')
469 submenu.show_all()
470 menu.set_submenu(submenu)
473 # Default Notebook
476 self.notebook.show()
477 self.pages = []
478 self.current_page = 0
479 self.last_page = 0
481 dict = {
482 'on_login_activate': self.sig_login,
483 'on_logout_activate': self.sig_logout,
484 'on_win_next_activate': self.sig_win_next,
485 'on_win_prev_activate': self.sig_win_prev,
486 'on_plugin_execute_activate': self.sig_plugin_execute,
487 'on_quit_activate': self.sig_close,
488 'on_but_menu_clicked': self.sig_win_menu,
489 'on_win_new_activate': self.sig_win_menu,
490 'on_win_home_activate': self.sig_home_new,
491 'on_win_close_activate': self.sig_win_close,
492 'on_support_activate': common.support,
493 'on_preference_activate': self.sig_user_preferences,
494 'on_read_requests_activate': self.sig_request_open,
495 'on_send_request_activate': self.sig_request_new,
496 'on_request_wait_activate': self.sig_request_wait,
497 'on_opt_save_activate': lambda x: options.options.save(),
498 'on_menubar_icons_activate': lambda x: self.sig_menubar('icons'),
499 'on_menubar_text_activate': lambda x: self.sig_menubar('text'),
500 'on_menubar_both_activate': lambda x: self.sig_menubar('both'),
501 'on_mode_normal_activate': lambda x: self.sig_mode_change(False),
502 'on_mode_pda_activate': lambda x: self.sig_mode_change(True),
503 'on_opt_form_tab_top_activate': lambda x: self.sig_form_tab('top'),
504 'on_opt_form_tab_left_activate': lambda x: self.sig_form_tab('left'),
505 'on_opt_form_tab_right_activate': lambda x: self.sig_form_tab('right'),
506 'on_opt_form_tab_bottom_activate': lambda x: self.sig_form_tab('bottom'),
507 'on_opt_form_tab_orientation_horizontal_activate': lambda x: self.sig_form_tab_orientation(0),
508 'on_opt_form_tab_orientation_vertical_activate': lambda x: self.sig_form_tab_orientation(90),
509 'on_help_index_activate': self.sig_help_index,
510 'on_help_contextual_activate': self.sig_help_context,
511 'on_help_licence_activate': self.sig_licence,
512 'on_about_activate': self.sig_about,
513 'on_shortcuts_activate' : self.sig_shortcuts,
514 'on_db_new_activate': self.sig_db_new,
515 'on_db_restore_activate': self.sig_db_restore,
516 'on_db_backup_activate': self.sig_db_dump,
517 'on_db_drop_activate': self.sig_db_drop,
518 'on_admin_password_activate': self.sig_db_password,
519 'on_extension_manager_activate': self.sig_extension_manager,
521 for signal in dict:
522 self.glade.signal_connect(signal, dict[signal])
524 self.buttons = {}
525 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'):
526 self.glade.signal_connect('on_'+button+'_clicked', self._sig_child_call, button)
527 self.buttons[button]=self.glade.get_widget(button)
529 menus = {
530 'form_del': 'but_remove',
531 'form_new': 'but_new',
532 'form_copy': 'but_copy',
533 'form_reload': 'but_reload',
534 'form_log': 'but_log',
535 'form_open': 'but_open',
536 'form_search': 'but_search',
537 'form_previous': 'but_previous',
538 'form_next': 'but_next',
539 'form_save': 'but_save',
540 'goto_id': 'but_goto_id',
541 'form_print': 'but_print',
542 'form_print_html': 'but_print_html',
543 'form_save_as': 'but_save_as',
544 'form_import': 'but_import',
545 'form_filter': 'but_filter',
546 'form_repeat': 'but_print_repeat'
548 for menu in menus:
549 self.glade.signal_connect('on_'+menu+'_activate', self._sig_child_call, menus[menu])
551 spool = service.LocalService('spool')
552 spool.subscribe('gui.window', self.win_add)
555 # we now create the icon for the attachment button when there are attachments
556 self.__img_no_attachments = gtk.Image()
557 pxbf = self.window.render_icon(self.buttons['but_attach'].get_stock_id(), self.toolbar.get_icon_size())
558 self.__img_no_attachments.set_from_pixbuf(pxbf)
559 self.__img_no_attachments.show()
561 pxbf = pxbf.copy()
562 w, h = pxbf.get_width(), pxbf.get_height()
563 overlay = self.window.render_icon(gtk.STOCK_APPLY, gtk.ICON_SIZE_MENU)
564 ow, oh = overlay.get_width(), overlay.get_height()
565 overlay.composite(pxbf,
566 0, h - oh,
567 ow, oh,
568 0, h - oh,
569 1.0, 1.0,
570 gtk.gdk.INTERP_NEAREST,
571 255)
573 self.__img_attachments = gtk.Image()
574 self.__img_attachments.set_from_pixbuf(pxbf)
575 self.__img_attachments.show()
577 self.sb_set()
579 settings = gtk.settings_get_default()
580 settings.set_long_property('gtk-button-images', 1, 'TinyERP:gui.main')
582 def fnc_menuitem(menuitem, opt_name):
583 options.options[opt_name] = menuitem.get_active()
584 dict = {
585 'on_opt_print_preview_activate': (fnc_menuitem, 'printer.preview', 'opt_print_preview'),
586 'on_opt_form_toolbar_activate': (fnc_menuitem, 'form.toolbar', 'opt_form_toolbar'),
588 self.glade.get_widget('menubar_'+(options.options['client.toolbar'] or 'both')).set_active(True)
589 self.sig_menubar(options.options['client.toolbar'] or 'both')
590 self.glade.get_widget('opt_form_tab_'+(options.options['client.form_tab'] or 'left')).set_active(True)
591 self.sig_form_tab(options.options['client.form_tab'] or 'left')
592 self.glade.get_widget('opt_form_tab_orientation_'+(str(options.options['client.form_tab_orientation']) or '0')).set_active(True)
593 self.sig_form_tab_orientation(options.options['client.form_tab_orientation'] or 0)
594 if options.options['client.modepda']:
595 self.glade.get_widget('mode_pda').set_active(True)
596 else:
597 self.glade.get_widget('mode_normal').set_active(True)
598 self.sig_mode()
599 for signal in dict:
600 self.glade.signal_connect(signal, dict[signal][0], dict[signal][1])
601 self.glade.get_widget(dict[signal][2]).set_active(int(bool(options.options[dict[signal][1]])))
603 # Adding a timer the check to requests
604 gobject.timeout_add(5 * 60 * 1000, self.request_set)
606 def shortcut_edit(self, widget, model='ir.ui.menu'):
607 obj = service.LocalService('gui.window')
608 domain = [('user_id', '=', rpc.session.uid), ('resource', '=', model)]
609 obj.create(False, 'ir.ui.view_sc', res_id=None, domain=domain, view_type='form', mode='tree,form')
611 def shortcut_set(self, sc=None):
612 def _action_shortcut(widget, action):
613 if action:
614 ctx = rpc.session.context.copy()
615 obj = service.LocalService('action.main')
616 obj.exec_keyword('tree_but_open', {'model': 'ir.ui.menu', 'id': action[0],
617 'ids': [action[0]], 'report_type': 'pdf', 'window': self.window}, context=ctx)
619 if sc is None:
620 uid = rpc.session.uid
621 sc = rpc.session.rpc_exec_auth('/object', 'execute', 'ir.ui.view_sc', 'get_sc', uid, 'ir.ui.menu', rpc.session.context) or []
623 menu = gtk.Menu()
624 for s in sc:
625 menuitem = gtk.MenuItem(s['name'])
626 menuitem.connect('activate', _action_shortcut, s['res_id'])
627 menu.add(menuitem)
629 menu.add(gtk.SeparatorMenuItem())
630 menuitem = gtk.MenuItem(_('Edit'))
631 menuitem.connect('activate', self.shortcut_edit)
632 menu.add(menuitem)
634 menu.show_all()
635 self.shortcut_menu.set_submenu(menu)
636 self.shortcut_menu.set_sensitive(True)
638 def shortcut_unset(self):
639 menu = gtk.Menu()
640 menu.show_all()
641 self.shortcut_menu.set_submenu(menu)
642 self.shortcut_menu.set_sensitive(False)
644 def theme_select(self, widget, theme):
645 options.options['client.theme'] = theme
646 common.theme_set()
647 self.window.show_all()
648 return True
650 def sig_mode_change(self, pda_mode=False):
651 options.options['client.modepda'] = pda_mode
652 return self.sig_mode()
654 def sig_mode(self):
655 pda_mode = options.options['client.modepda']
656 if pda_mode:
657 self.status_bar_main.hide()
658 else:
659 self.status_bar_main.show()
660 return pda_mode
662 def sig_menubar(self, option):
663 options.options['client.toolbar'] = option
664 if option=='both':
665 self.toolbar.set_style(gtk.TOOLBAR_BOTH)
666 elif option=='text':
667 self.toolbar.set_style(gtk.TOOLBAR_TEXT)
668 elif option=='icons':
669 self.toolbar.set_style(gtk.TOOLBAR_ICONS)
671 def sig_form_tab(self, option):
672 options.options['client.form_tab'] = option
674 def sig_form_tab_orientation(self, option):
675 options.options['client.form_tab_orientation'] = option
677 def sig_win_next(self, args):
678 pn = self.notebook.get_current_page()
679 if pn == len(self.pages)-1:
680 pn = -1
681 self.notebook.set_current_page(pn+1)
683 def sig_win_prev(self, args):
684 pn = self.notebook.get_current_page()
685 self.notebook.set_current_page(pn-1)
687 def sig_user_preferences(self, *args):
688 win =win_preference.win_preference(parent=self.window)
689 win.run()
690 return True
692 def sig_win_close(self, *args):
693 self._sig_child_call(args[0], 'but_close')
695 def sig_request_new(self, args=None):
696 obj = service.LocalService('gui.window')
697 try:
698 return obj.create(None, 'res.request', False,
699 [('act_from', '=', rpc.session.uid)], 'form',
700 mode='form,tree', window=self.window,
701 context={'active_test': False})
702 except:
703 return False
705 def sig_request_open(self, args=None):
706 ids,ids2 = self.request_set()
707 obj = service.LocalService('gui.window')
708 try:
709 return obj.create(False, 'res.request', ids,
710 [('act_to', '=', rpc.session.uid), ('active', '=', True)],
711 'form', mode='tree,form', window=self.window,
712 context={'active_test': False})
713 except:
714 return False
716 def sig_request_wait(self, args=None):
717 ids,ids2 = self.request_set()
718 obj = service.LocalService('gui.window')
719 try:
720 return obj.create(False, 'res.request', ids,
721 [('act_from', '=', rpc.session.uid),
722 ('state', '=', 'waiting'), ('active', '=', True)],
723 'form', mode='tree,form', window=self.window,
724 context={'active_test': False})
725 except:
726 return False
728 def request_set(self):
729 try:
730 uid = rpc.session.uid
731 ids,ids2 = rpc.session.rpc_exec_auth_try('/object', 'execute',
732 'res.request', 'request_get')
733 if len(ids):
734 message = _('%s request(s)') % len(ids)
735 else:
736 message = _('No request')
737 if len(ids2):
738 message += _(' - %s request(s) sended') % len(ids2)
739 id = self.sb_requests.get_context_id('message')
740 self.sb_requests.push(id, message)
741 return (ids,ids2)
742 except:
743 return ([],[])
745 def sig_login(self, widget=None, dbname=False):
746 RES_OK = 1
747 RES_BAD_PASSWORD = -2
748 RES_CNX_ERROR = -1
749 try:
750 log_response = RES_BAD_PASSWORD
751 res = None
752 while log_response == RES_BAD_PASSWORD:
753 try:
754 l = db_login()
755 res = l.run(dbname=dbname, parent=self.window)
756 except Exception, e:
757 if e.args == ('QueryCanceled',):
758 return False
759 raise
760 service.LocalService('gui.main').window.present()
761 self.sig_logout(widget)
762 log_response = rpc.session.login(*res)
763 if log_response == RES_OK:
764 options.options.save()
765 id = self.sig_win_menu(quiet=False)
766 if id:
767 self.sig_home_new(quiet=True, except_id=id)
768 if res[4] == 'https://':
769 self.secure_img.show()
770 else:
771 self.secure_img.hide()
772 self.request_set()
773 elif log_response == RES_CNX_ERROR:
774 common.message(_('Connection error !\nUnable to connect to the server !'))
775 elif log_response == RES_BAD_PASSWORD:
776 common.message(_('Connection error !\nBad username or password !'))
777 except rpc.rpc_exception:
778 rpc.session.logout()
779 raise
780 self.glade.get_widget('but_menu').set_sensitive(True)
781 self.glade.get_widget('user').set_sensitive(True)
782 self.glade.get_widget('form').set_sensitive(True)
783 self.glade.get_widget('plugins').set_sensitive(True)
784 return True
786 def sig_logout(self, widget):
787 res = True
788 while res:
789 wid = self._wid_get()
790 if wid:
791 if 'but_close' in wid.handlers:
792 res = wid.handlers['but_close']()
793 if not res:
794 return False
795 res = self._win_del()
796 else:
797 res = False
798 id = self.sb_requests.get_context_id('message')
799 self.sb_requests.push(id, '')
800 id = self.sb_username.get_context_id('message')
801 self.sb_username.push(id, _('Not logged !'))
802 id = self.sb_servername.get_context_id('message')
803 self.sb_servername.push(id, _('Press Ctrl+O to login'))
804 self.secure_img.hide()
805 self.shortcut_unset()
806 self.glade.get_widget('but_menu').set_sensitive(False)
807 self.glade.get_widget('user').set_sensitive(False)
808 self.glade.get_widget('form').set_sensitive(False)
809 self.glade.get_widget('plugins').set_sensitive(False)
810 rpc.session.logout()
811 return True
813 def sig_help_index(self, widget):
814 tools.launch_browser(options.options['help.index'])
816 def sig_help_context(self, widget):
817 model = self._wid_get().model
818 l = rpc.session.context.get('lang','en_US')
819 tools.launch_browser(options.options['help.context']+'?model=%s&lang=%s' % (model,l))
821 def sig_licence(self, widget):
822 dialog = glade.XML(common.terp_path("terp.glade"), "win_licence", gettext.textdomain())
823 dialog.signal_connect("on_but_ok_pressed", lambda obj: dialog.get_widget('win_licence').destroy())
825 win = dialog.get_widget('win_licence')
826 win.set_transient_for(self.window)
827 win.show_all()
829 def sig_about(self, widget):
830 about = glade.XML(common.terp_path("terp.glade"), "win_about", gettext.textdomain())
831 buffer = about.get_widget('textview2').get_buffer()
832 about_txt = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter())
833 buffer.set_text(about_txt % tinyerp_version)
834 about.signal_connect("on_but_ok_pressed", lambda obj: about.get_widget('win_about').destroy())
836 win = about.get_widget('win_about')
837 win.set_transient_for(self.window)
838 win.show_all()
840 def sig_shortcuts(self, widget):
841 shortcuts_win = glade.XML(common.terp_path('terp.glade'), 'shortcuts_dia', gettext.textdomain())
842 shortcuts_win.signal_connect("on_but_ok_pressed", lambda obj: shortcuts_win.get_widget('shortcuts_dia').destroy())
844 win = shortcuts_win.get_widget('shortcuts_dia')
845 win.set_transient_for(self.window)
846 win.show_all()
848 def sig_win_menu(self, widget=None, quiet=True):
849 for p in range(len(self.pages)):
850 if self.pages[p].model=='ir.ui.menu':
851 self.notebook.set_current_page(p)
852 return True
853 res = self.sig_win_new(widget, type='menu_id', quiet=quiet)
854 if not res:
855 return self.sig_win_new(widget, type='action_id', quiet=quiet)
856 return res
858 def sig_win_new(self, widget=None, type='menu_id', quiet=True, except_id=False):
859 try:
860 act_id = rpc.session.rpc_exec_auth('/object', 'execute', 'res.users',
861 'read', [rpc.session.uid], [type,'name'], rpc.session.context)
862 except:
863 return False
864 id = self.sb_username.get_context_id('message')
865 self.sb_username.push(id, act_id[0]['name'] or '')
866 id = self.sb_servername.get_context_id('message')
867 data = urlparse.urlsplit(rpc.session._url)
868 self.sb_servername.push(id, data[0]+':'+(data[1] and '//'+data[1] \
869 or data[2])+' ['+options.options['login.db']+']')
870 if not act_id[0][type]:
871 if quiet:
872 return False
873 common.warning(_("You can not log into the system !\nAsk the administrator to verify\nyou have an action defined for your user."),'Access Denied !')
874 rpc.session.logout()
875 return False
876 act_id = act_id[0][type][0]
877 if except_id and act_id == except_id:
878 return act_id
879 obj = service.LocalService('action.main')
880 win = obj.execute(act_id, {'window':self.window})
881 try:
882 user = rpc.session.rpc_exec_auth_wo('/object', 'execute', 'res.users',
883 'read', [rpc.session.uid], [type,'name'], rpc.session.context)
884 if user[0][type]:
885 act_id = user[0][type][0]
886 except:
887 pass
888 return act_id
890 def sig_home_new(self, widget=None, quiet=True, except_id=False):
891 return self.sig_win_new(widget, type='action_id', quiet=quiet,
892 except_id=except_id)
894 def sig_plugin_execute(self, widget):
895 import plugins
896 pn = self.notebook.get_current_page()
897 datas = {'model': self.pages[pn].model, 'ids':self.pages[pn].ids_get(), 'id' : self.pages[pn].id_get()}
898 plugins.execute(datas)
900 def sig_quit(self, widget):
901 options.options.save()
902 gtk.main_quit()
904 def sig_close(self, widget):
905 if common.sur(_("Do you really want to quit ?"), parent=self.window):
906 if not self.sig_logout(widget):
907 return False
908 options.options.save()
909 gtk.main_quit()
911 def sig_delete(self, widget, event, data=None):
912 if common.sur(_("Do you really want to quit ?"), parent=self.window):
913 if not self.sig_logout(widget):
914 return True
915 return False
916 return True
918 def win_add(self, win, datas):
919 self.pages.append(win)
920 self.notebook.append_page(win.widget, gtk.Label(win.name))
921 self.notebook.set_current_page(-1)
923 def message(self, message):
924 id = self.status_bar.get_context_id('message')
925 self.status_bar.push(id, message)
927 def __attachment_callback(self, view, objid):
928 current_view = self._wid_get()
929 current_id = current_view and current_view.id_get()
930 if current_view == view and objid == current_id:
931 cpt = None
932 if objid and view.screen.current_view.view_type == 'form':
933 cpt = rpc.session.rpc_exec_auth('/object', 'execute',
934 'ir.attachment', 'search_count',
935 [('res_model', '=', view.model), ('res_id', '=', objid)])
936 if cpt:
937 self.buttons['but_attach'].set_icon_widget(self.__img_attachments)
938 self.buttons['but_attach'].set_label(_('Attachments (%d)') % cpt)
941 def _update_attachment_button(self, view = None):
943 Update the attachment icon for display the number of attachments
945 if not view:
946 view = self._wid_get()
948 id = view and view.id_get()
949 gobject.timeout_add(1500, self.__attachment_callback, view, id)
950 self.buttons['but_attach'].set_icon_widget(self.__img_no_attachments)
951 self.buttons['but_attach'].set_label(_('Attachments'))
954 def sb_set(self, view=None):
955 if not view:
956 view = self._wid_get()
957 if view and hasattr(view, 'screen'):
958 self._handler_ok = False
959 self.glade.get_widget('radio_'+view.screen.current_view.view_type).set_active(True)
960 self._handler_ok = True
961 self._update_attachment_button(view)
962 for x in self.buttons:
963 if self.buttons[x]:
964 self.buttons[x].set_sensitive(view and (x in view.handlers))
966 def _win_del(self):
967 pn = self.notebook.get_current_page()
968 if pn != -1:
969 self.notebook.disconnect(self.sig_id)
970 page = self.pages.pop(pn)
971 self.notebook.remove_page(pn)
972 self.sig_id = self.notebook.connect_after('switch-page', self._sig_page_changt)
973 self.sb_set()
975 page.destroy()
976 del page
977 return self.notebook.get_current_page() != -1
979 def _wid_get(self):
980 pn = self.notebook.get_current_page()
981 if pn == -1:
982 return False
983 return self.pages[pn]
985 def _sig_child_call(self, widget, button_name, *args):
986 if not self._handler_ok:
987 return
988 wid = self._wid_get()
989 if wid:
990 res = True
991 if button_name.startswith('radio_'):
992 act = self.glade.get_widget(button_name).get_active()
993 if not act: return False
995 if button_name in wid.handlers:
996 res = wid.handlers[button_name]()
997 # for those buttons, we refresh the attachment button.
998 # for the "switch view" button, the action has already
999 # been called by the Screen object of the view (wid)
1000 if button_name in ('but_new', 'but_remove', 'but_search', \
1001 'but_previous', 'but_next', 'but_open', \
1002 'but_close', 'but_reload', 'but_attach', 'but_goto_id'):
1003 self._update_attachment_button(wid)
1004 if button_name.startswith('radio_'):
1005 pass
1006 #mode = wid.screen.current_view.view_type
1007 #print 'RADIO CALLED', button_name, args, self.glade.get_widget(button_name).get_active(), button_name[6:]
1008 #if self.glade.get_widget(button_name).get_active() and (mode<>button_name[6:]):
1009 # print 'SWITCH', button_name
1010 # wid.sig_switch()
1011 if button_name=='but_close' and res:
1012 self._win_del()
1014 def _sig_page_changt(self, widget=None, *args):
1015 self.last_page = self.current_page
1016 self.current_page = self.notebook.get_current_page()
1017 self.sb_set()
1019 def sig_db_new(self, widget):
1020 if not self.sig_logout(widget):
1021 return False
1022 dia = db_create(self.sig_login, self)
1023 res = dia.run(self.window)
1024 if res:
1025 options.options.save()
1026 return res
1028 def sig_db_drop(self, widget):
1029 if not self.sig_logout(widget):
1030 return False
1031 url, db_name, passwd = self._choose_db_select(_('Delete a database'))
1032 if not db_name:
1033 return
1035 try:
1036 rpc.session.db_exec(url, 'drop', passwd, db_name)
1037 common.message(_("Database dropped successfully !"), parent=self.window)
1038 except Exception, e:
1039 if ('faultString' in e and e.faultString=='AccessDenied:None') or str(e)=='AccessDenied':
1040 common.warning(_('Bad database administrator password !'),_("Could not drop database."), parent=self.window)
1041 else:
1042 common.warning(_("Couldn't drop database"), parent=self.window)
1044 def sig_db_restore(self, widget):
1045 filename = common.file_selection(_('Open...'), parent=self.window, preview=False)
1046 if not filename:
1047 return
1049 url, db_name, passwd = self._choose_db_ent()
1050 if db_name:
1051 try:
1052 f = file(filename, 'rb')
1053 data_b64 = base64.encodestring(f.read())
1054 f.close()
1055 rpc.session.db_exec(url, 'restore', passwd, db_name, data_b64)
1056 common.message(_("Database restored successfully !"), parent=self.window)
1057 except Exception,e:
1058 if ('faultString' in e and e.faultString=='AccessDenied:None') or str(e)=='AccessDenied':
1059 common.warning(_('Bad database administrator password !'),_("Could not restore database."), parent=self.window)
1060 else:
1061 common.warning(_("Couldn't restore database"), parent=self.window)
1063 def sig_extension_manager(self,widget):
1064 win = win_extension.win_extension(self.window)
1065 win.run()
1067 def sig_db_password(self, widget):
1068 dialog = glade.XML(common.terp_path("terp.glade"), "dia_passwd_change",
1069 gettext.textdomain())
1070 win = dialog.get_widget('dia_passwd_change')
1071 win.set_icon(common.TINYERP_ICON)
1072 win.set_transient_for(self.window)
1073 win.show_all()
1074 server_widget = dialog.get_widget('ent_server2')
1075 old_pass_widget = dialog.get_widget('old_passwd')
1076 new_pass_widget = dialog.get_widget('new_passwd')
1077 new_pass2_widget = dialog.get_widget('new_passwd2')
1078 change_button = dialog.get_widget('but_server_change1')
1079 change_button.connect_after('clicked', lambda a,b: _server_ask(b, win), server_widget)
1081 host = options.options['login.server']
1082 port = options.options['login.port']
1083 protocol = options.options['login.protocol']
1084 url = '%s%s:%s' % (protocol, host, port)
1085 server_widget.set_text(url)
1087 res = win.run()
1088 if res == gtk.RESPONSE_OK:
1089 url = server_widget.get_text()
1090 old_passwd = old_pass_widget.get_text()
1091 new_passwd = new_pass_widget.get_text()
1092 new_passwd2 = new_pass2_widget.get_text()
1093 if new_passwd != new_passwd2:
1094 common.warning(_("Confirmation password do not match " \
1095 "new password, operation cancelled!"),
1096 _("Validation Error."), parent=win)
1097 else:
1098 try:
1099 rpc.session.db_exec(url, 'change_admin_password',
1100 old_passwd, new_passwd)
1101 except Exception,e:
1102 if ('faultString' in e and e.faultString=='AccessDenied:None') \
1103 or str(e)=='AccessDenied':
1104 common.warning(_("Could not change password database."),
1105 _('Bas password provided !'), parent=win)
1106 else:
1107 common.warning(_("Error, password not changed."),
1108 parent=win)
1109 self.window.present()
1110 win.destroy()
1112 def sig_db_dump(self, widget):
1113 url, db_name, passwd = self._choose_db_select(_('Backup a database'))
1114 if not db_name:
1115 return
1116 filename = common.file_selection(_('Save As...'),
1117 action=gtk.FILE_CHOOSER_ACTION_SAVE, parent=self.window, preview=False)
1119 if filename:
1120 try:
1121 dump_b64 = rpc.session.db_exec(url, 'dump', passwd, db_name)
1122 dump = base64.decodestring(dump_b64)
1123 f = file(filename, 'wb')
1124 f.write(dump)
1125 f.close()
1126 common.message(_("Database backuped successfully !"), parent=self.window)
1127 except:
1128 common.warning(_("Couldn't backup database."), parent=self.window)
1130 def _choose_db_select(self, title=_("Backup a database")):
1131 def refreshlist(widget, db_widget, label, url):
1132 res = _refresh_dblist(db_widget, url)
1133 if res == -1:
1134 label.set_label('<b>'+_('Could not connect to server !')+'</b>')
1135 db_widget.hide()
1136 label.show()
1137 elif res==0:
1138 label.set_label('<b>'+_('No database found, you must create one !')+'</b>')
1139 db_widget.hide()
1140 label.show()
1141 else:
1142 label.hide()
1143 db_widget.show()
1144 return res
1146 def refreshlist_ask(widget, server_widget, db_widget, label, parent=None):
1147 url = _server_ask(server_widget, parent)
1148 if not url:
1149 return None
1150 refreshlist(widget, db_widget, label, url)
1151 return url
1153 dialog = glade.XML(common.terp_path("terp.glade"), "win_db_select",
1154 gettext.textdomain())
1155 win = dialog.get_widget('win_db_select')
1156 win.set_icon(common.TINYERP_ICON)
1157 win.set_default_response(gtk.RESPONSE_OK)
1158 win.set_transient_for(self.window)
1159 win.show_all()
1161 pass_widget = dialog.get_widget('ent_passwd_select')
1162 server_widget = dialog.get_widget('ent_server_select')
1163 db_widget = dialog.get_widget('combo_db_select')
1164 label = dialog.get_widget('label_db_select')
1167 dialog.get_widget('db_select_label').set_markup('<b>'+title+'</b>')
1169 protocol = options.options['login.protocol']
1170 url = '%s%s:%s' % (protocol, options.options['login.server'], options.options['login.port'])
1171 server_widget.set_text(url)
1173 liststore = gtk.ListStore(str)
1174 db_widget.set_model(liststore)
1176 refreshlist(None, db_widget, label, url)
1177 change_button = dialog.get_widget('but_server_select')
1178 change_button.connect_after('clicked', refreshlist_ask, server_widget, db_widget, label, win)
1180 cell = gtk.CellRendererText()
1181 db_widget.pack_start(cell, True)
1182 db_widget.add_attribute(cell, 'text', 0)
1184 res = win.run()
1186 db = False
1187 url = False
1188 passwd = False
1189 if res == gtk.RESPONSE_OK:
1190 db = db_widget.get_active_text()
1191 url = server_widget.get_text()
1192 passwd = pass_widget.get_text()
1193 self.window.present()
1194 win.destroy()
1195 return (url,db,passwd)
1197 def _choose_db_ent(self):
1198 dialog = glade.XML(common.terp_path("terp.glade"), "win_db_ent",
1199 gettext.textdomain())
1200 win = dialog.get_widget('win_db_ent')
1201 win.set_icon(common.TINYERP_ICON)
1202 win.set_transient_for(self.window)
1203 win.show_all()
1205 db_widget = dialog.get_widget('ent_db')
1206 widget_pass = dialog.get_widget('ent_password')
1207 widget_url = dialog.get_widget('ent_server1')
1209 protocol = options.options['login.protocol']
1210 url = '%s%s:%s' % (protocol, options.options['login.server'],
1211 options.options['login.port'])
1212 widget_url.set_text(url)
1214 change_button = dialog.get_widget('but_server_change')
1215 change_button.connect_after('clicked', lambda a,b: _server_ask(b, win),
1216 widget_url)
1218 res = win.run()
1220 db = False
1221 passwd = False
1222 url = False
1223 if res == gtk.RESPONSE_OK:
1224 db = db_widget.get_text()
1225 url = widget_url.get_text()
1226 passwd = widget_pass.get_text()
1227 self.window.present()
1228 win.destroy()
1229 return url, db, passwd
1232 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: