Files
tryton/tryton/gui/main.py
T
Cédric Krier e1c3caae8e Always pass string to urllib.unquote
When there is no trailing '/', it adds None which is not supported by
urllib.unquote.

issue4589
review12981002
2015-03-02 23:02:34 +01:00

1555 lines
60 KiB
Python

# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
import os
import sys
import gettext
from urlparse import urlparse, parse_qsl
import urllib
import gobject
import gtk
try:
import simplejson as json
except ImportError:
import json
import webbrowser
import threading
import tryton.rpc as rpc
from tryton.common import RPCExecute, RPCException, RPCContextReload
from tryton.config import CONFIG, TRYTON_ICON, get_config_dir
import tryton.common as common
from tryton.pyson import PYSONDecoder
from tryton.jsonrpc import object_hook
from tryton.action import Action
from tryton.exceptions import TrytonServerError, TrytonError, \
TrytonServerUnavailable
from tryton.gui.window import Window
from tryton.gui.window.preference import Preference
from tryton.gui.window import Limit
from tryton.gui.window import Email
from tryton.gui.window.dblogin import DBLogin
from tryton.gui.window.dbcreate import DBCreate
from tryton.gui.window.dbdumpdrop import DBBackupDrop
from tryton.gui.window.tips import Tips
from tryton.gui.window.about import About
from tryton.gui.window.shortcuts import Shortcuts
from tryton.gui.window.dbrestore import DBRestore
from tryton.common.cellrendererclickablepixbuf import \
CellRendererClickablePixbuf
import tryton.translate as translate
import tryton.plugins
from tryton.common.placeholder_entry import PlaceholderEntry
import pango
import time
try:
import gtk_osxapplication
except ImportError:
gtk_osxapplication = None
try:
import gtkspell
except ImportError:
gtkspell = None
_ = gettext.gettext
_MAIN = []
TAB_SIZE = 120
class Main(object):
window = None
tryton_client = None
def __init__(self, tryton_client):
super(Main, self).__init__()
Main.tryton_client = tryton_client
self.window = gtk.Window()
self._width = int(CONFIG['client.default_width'])
self._height = int(CONFIG['client.default_height'])
if CONFIG['client.maximize']:
self.window.maximize()
self.window.set_default_size(self._width, self._height)
self.window.set_resizable(True)
self.set_title()
self.window.set_icon(TRYTON_ICON)
self.window.connect("destroy", Main.sig_quit)
self.window.connect("delete_event", self.sig_close)
self.window.connect('configure_event', self.sig_configure)
self.window.connect('window_state_event', self.sig_window_state)
self.accel_group = gtk.AccelGroup()
self.window.add_accel_group(self.accel_group)
self.macapp = None
if gtk_osxapplication is not None:
self.macapp = gtk_osxapplication.OSXApplication()
self.macapp.connect("NSApplicationBlockTermination",
self.sig_close)
gtk.accel_map_add_entry('<tryton>/File/Connect', gtk.keysyms.O,
gtk.gdk.CONTROL_MASK)
gtk.accel_map_add_entry('<tryton>/File/Quit', gtk.keysyms.Q,
gtk.gdk.CONTROL_MASK)
if sys.platform != 'darwin':
gtk.accel_map_add_entry('<tryton>/User/Menu Reload', gtk.keysyms.T,
gtk.gdk.MOD1_MASK)
gtk.accel_map_add_entry('<tryton>/User/Menu Toggle', gtk.keysyms.T,
gtk.gdk.CONTROL_MASK)
gtk.accel_map_add_entry('<tryton>/User/Global Search', gtk.keysyms.K,
gtk.gdk.CONTROL_MASK)
gtk.accel_map_add_entry('<tryton>/User/Home', gtk.keysyms.H,
gtk.gdk.CONTROL_MASK)
gtk.accel_map_add_entry('<tryton>/Form/New', gtk.keysyms.N,
gtk.gdk.CONTROL_MASK)
gtk.accel_map_add_entry('<tryton>/Form/Save', gtk.keysyms.S,
gtk.gdk.CONTROL_MASK)
gtk.accel_map_add_entry('<tryton>/Form/Duplicate', gtk.keysyms.D,
gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK)
gtk.accel_map_add_entry('<tryton>/Form/Delete', gtk.keysyms.D,
gtk.gdk.CONTROL_MASK)
gtk.accel_map_add_entry('<tryton>/Form/Next', gtk.keysyms.Page_Down,
0)
gtk.accel_map_add_entry('<tryton>/Form/Previous', gtk.keysyms.Page_Up,
0)
gtk.accel_map_add_entry('<tryton>/Form/Switch View', gtk.keysyms.L,
gtk.gdk.CONTROL_MASK)
gtk.accel_map_add_entry('<tryton>/Form/Close', gtk.keysyms.W,
gtk.gdk.CONTROL_MASK)
gtk.accel_map_add_entry('<tryton>/Form/Previous Tab',
gtk.keysyms.Page_Up, gtk.gdk.CONTROL_MASK)
gtk.accel_map_add_entry('<tryton>/Form/Next Tab',
gtk.keysyms.Page_Down, gtk.gdk.CONTROL_MASK)
gtk.accel_map_add_entry('<tryton>/Form/Reload', gtk.keysyms.R,
gtk.gdk.CONTROL_MASK)
gtk.accel_map_add_entry('<tryton>/Form/Actions', gtk.keysyms.E,
gtk.gdk.CONTROL_MASK)
gtk.accel_map_add_entry('<tryton>/Form/Report', gtk.keysyms.P,
gtk.gdk.CONTROL_MASK)
gtk.accel_map_add_entry('<tryton>/Form/Search', gtk.keysyms.F,
gtk.gdk.CONTROL_MASK)
gtk.accel_map_load(os.path.join(get_config_dir(), 'accel.map'))
self.tooltips = common.Tooltips()
self.vbox = gtk.VBox()
self.window.add(self.vbox)
self.menubar = None
self.global_search_entry = None
self.menuitem_user = None
self.menuitem_favorite = None
self.buttons = {}
self.pane = gtk.HPaned()
self.pane.connect('button-press-event',
self.on_paned_button_press_event)
self.menu_screen = None
self.menu_expander = gtk.Expander()
self.menu_expander.connect('notify::expanded', self.menu_expanded)
if self.menu_expander.get_direction() == gtk.TEXT_DIR_RTL:
self.menu_expander.set_direction(gtk.TEXT_DIR_LTR)
else:
self.menu_expander.set_direction(gtk.TEXT_DIR_RTL)
self.menu_expander.set_expanded(CONFIG['menu.expanded'])
self.pane.add(self.menu_expander)
self.vbox.pack_start(self.pane, True, True)
self.notebook = gtk.Notebook()
self.notebook.popup_enable()
self.notebook.set_scrollable(True)
self.notebook.connect_after('switch-page', self._sig_page_changt)
self.pane.add2(self.notebook)
self.set_menubar()
self.window.show_all()
self.pages = []
self.previous_pages = {}
self.current_page = 0
self.last_page = 0
self.dialogs = []
if CONFIG['client.modepda']:
self.radiomenuitem_pda.set_active(True)
else:
self.radiomenuitem_normal.set_active(True)
settings = gtk.settings_get_default()
# Due to a bug in old version of pyGTk gtk-button-images can
# not be set when there is no buttons
gtk.Button()
try:
settings.set_property('gtk-button-images', True)
except TypeError:
pass
try:
settings.set_property('gtk-can-change-accels',
CONFIG['client.can_change_accelerators'])
except TypeError:
pass
# Register plugins
tryton.plugins.register()
if self.macapp is not None:
self.macapp.ready()
_MAIN.append(self)
def set_menubar(self):
if self.menubar:
self.menubar.destroy()
menubar = gtk.MenuBar()
self.menubar = menubar
self.vbox.pack_start(menubar, False, True)
self.vbox.reorder_child(menubar, 0)
menuitem_file = gtk.MenuItem(_('_File'))
menubar.add(menuitem_file)
menu_file = self._set_menu_file()
menuitem_file.set_submenu(menu_file)
menu_file.set_accel_group(self.accel_group)
menu_file.set_accel_path('<tryton>/File')
menuitem_user = gtk.MenuItem(_('_User'))
if self.menuitem_user:
menuitem_user.set_sensitive(
self.menuitem_user.get_property('sensitive'))
else:
menuitem_user.set_sensitive(False)
self.menuitem_user = menuitem_user
menubar.add(menuitem_user)
menu_user = self._set_menu_user()
menuitem_user.set_submenu(menu_user)
menu_user.set_accel_group(self.accel_group)
menu_user.set_accel_path('<tryton>/User')
menuitem_options = gtk.MenuItem(_('_Options'))
menubar.add(menuitem_options)
menu_options = self._set_menu_options()
menuitem_options.set_submenu(menu_options)
menu_options.set_accel_group(self.accel_group)
menu_options.set_accel_path('<tryton>/Options')
menuitem_favorite = gtk.MenuItem(_('Fa_vorites'))
if self.menuitem_favorite:
menuitem_favorite.set_sensitive(
self.menuitem_favorite.get_property('sensitive'))
else:
menuitem_favorite.set_sensitive(False)
self.menuitem_favorite = menuitem_favorite
menubar.add(menuitem_favorite)
menuitem_favorite.set_accel_path('<tryton>/Favorites')
def favorite_activate(widget):
if (not menuitem_favorite.get_submenu()
or not menuitem_favorite.get_submenu().get_children()):
self.favorite_set()
menuitem_favorite.connect('select', favorite_activate)
menuitem_help = gtk.MenuItem(_('_Help'))
menubar.add(menuitem_help)
menu_help = self._set_menu_help()
menuitem_help.set_submenu(menu_help)
menu_help.set_accel_group(self.accel_group)
menu_help.set_accel_path('<tryton>/Help')
if self.macapp is not None:
self.menubar.set_no_show_all(True)
self.macapp.set_menu_bar(self.menubar)
self.macapp.insert_app_menu_item(self.aboutitem, 0)
menuitem_file.show_all()
menuitem_user.show_all()
menuitem_options.show_all()
menuitem_favorite.show_all()
menuitem_help.show_all()
else:
self.menubar.show_all()
def set_global_search(self):
self.global_search_entry = PlaceholderEntry()
self.global_search_entry.set_placeholder_text(_('Search'))
global_search_completion = gtk.EntryCompletion()
global_search_completion.set_match_func(lambda *a: True)
global_search_completion.set_model(gtk.ListStore(
gtk.gdk.Pixbuf, str, str, int))
pixbuf_cell = gtk.CellRendererPixbuf()
global_search_completion.pack_start(pixbuf_cell, False)
global_search_completion.add_attribute(pixbuf_cell, 'pixbuf', 0)
text_cell = gtk.CellRendererText()
global_search_completion.pack_start(text_cell)
global_search_completion.add_attribute(text_cell, "markup", 1)
global_search_completion.props.popup_set_width = True
self.global_search_entry.set_completion(global_search_completion)
self.global_search_entry.set_icon_from_stock(gtk.ENTRY_ICON_PRIMARY,
'gtk-find')
def match_selected(completion, model, iter_):
model, record_id = model.get(iter_, 2, 3)
if model == self.menu_screen.model_name:
Action.exec_keyword('tree_open', {
'model': model,
'id': record_id,
'ids': [record_id],
}, context=self.menu_screen.context.copy())
else:
Window.create(False, model, res_id=record_id,
mode=['form', 'tree'])
self.global_search_entry.set_text('')
return True
global_search_completion.connect('match-selected', match_selected)
def update(widget, search_text, callback=None):
def end():
if callback:
callback()
return False
if search_text != widget.get_text().decode('utf-8'):
return end()
gmodel = global_search_completion.get_model()
if not search_text or not gmodel:
gmodel.clear()
gmodel.search_text = search_text
return end()
if getattr(gmodel, 'search_text', None) == search_text:
return end()
def set_result(result):
try:
result = result()
except RPCException:
result = []
if search_text != widget.get_text().decode('utf-8'):
if callback:
callback()
return False
gmodel.clear()
for r in result:
_, model, model_name, record_id, record_name, icon = r
if icon:
text = common.to_xml(record_name)
common.ICONFACTORY.register_icon(icon)
pixbuf = widget.render_icon(stock_id=icon,
size=gtk.ICON_SIZE_BUTTON, detail=None)
else:
text = '<b>%s:</b>\n %s' % (
common.to_xml(model_name),
common.to_xml(record_name))
pixbuf = None
gmodel.append([pixbuf, text, model, record_id])
gmodel.search_text = search_text
# Force display of popup
widget.emit('changed')
end()
RPCExecute('model', 'ir.model', 'global_search', search_text,
CONFIG['client.limit'], self.menu_screen.model_name,
context=self.menu_screen.context, callback=set_result)
return False
def changed(widget):
search_text = widget.get_text().decode('utf-8')
gobject.timeout_add(300, update, widget, search_text)
def activate(widget):
def message():
gmodel = global_search_completion.get_model()
if not len(gmodel):
common.message(_('No result found.'))
else:
widget.emit('changed')
search_text = widget.get_text().decode('utf-8')
update(widget, search_text, message)
self.global_search_entry.connect('changed', changed)
self.global_search_entry.connect('activate', activate)
def show_global_search(self):
self.pane.get_child1().set_expanded(True)
self.global_search_entry.grab_focus()
def set_title(self, value=''):
title = 'Tryton'
if value:
title += ' - ' + value
self.window.set_title(title)
def _set_menu_file(self):
menu_file = gtk.Menu()
imagemenuitem_connect = gtk.ImageMenuItem(_('_Connect...'),
self.accel_group)
image = gtk.Image()
image.set_from_stock('tryton-connect', gtk.ICON_SIZE_MENU)
imagemenuitem_connect.set_image(image)
imagemenuitem_connect.connect('activate', self.sig_login)
imagemenuitem_connect.set_accel_path('<tryton>/File/Connect')
menu_file.add(imagemenuitem_connect)
imagemenuitem_disconnect = gtk.ImageMenuItem(_('_Disconnect'))
image = gtk.Image()
image.set_from_stock('tryton-disconnect', gtk.ICON_SIZE_MENU)
imagemenuitem_disconnect.set_image(image)
imagemenuitem_disconnect.connect('activate', self.sig_logout)
imagemenuitem_disconnect.set_accel_path('<tryton>/File/Disconnect')
menu_file.add(imagemenuitem_disconnect)
menu_file.add(gtk.SeparatorMenuItem())
imagemenuitem_database = gtk.ImageMenuItem(_('Data_base'))
image = gtk.Image()
image.set_from_stock('tryton-system-file-manager', gtk.ICON_SIZE_MENU)
imagemenuitem_database.set_image(image)
menu_file.add(imagemenuitem_database)
menu_database = gtk.Menu()
menu_database.set_accel_group(self.accel_group)
menu_database.set_accel_path('<tryton>/File/Database')
imagemenuitem_database.set_submenu(menu_database)
imagemenuitem_db_new = gtk.ImageMenuItem(_('_New Database...'))
image = gtk.Image()
image.set_from_stock('tryton-folder-new', gtk.ICON_SIZE_MENU)
imagemenuitem_db_new.set_image(image)
imagemenuitem_db_new.connect('activate', self.sig_db_new)
imagemenuitem_db_new.set_accel_path(
'<tryton>/File/Database/New Database')
menu_database.add(imagemenuitem_db_new)
imagemenuitem_db_restore = gtk.ImageMenuItem(_('_Restore Database...'))
image = gtk.Image()
image.set_from_stock('tryton-folder-saved-search', gtk.ICON_SIZE_MENU)
imagemenuitem_db_restore.set_image(image)
imagemenuitem_db_restore.connect('activate', self.sig_db_restore)
imagemenuitem_db_restore.set_accel_path(
'<tryton>/File/Database/Restore Database')
menu_database.add(imagemenuitem_db_restore)
imagemenuitem_db_dump = gtk.ImageMenuItem(_('_Backup Database...'))
image = gtk.Image()
image.set_from_stock('tryton-save-as', gtk.ICON_SIZE_MENU)
imagemenuitem_db_dump.set_image(image)
imagemenuitem_db_dump.connect('activate', self.sig_db_dump)
imagemenuitem_db_dump.set_accel_path(
'<tryton>/File/Database/Backup Database')
menu_database.add(imagemenuitem_db_dump)
imagemenuitem_db_drop = gtk.ImageMenuItem(_('Dro_p Database...'))
image = gtk.Image()
image.set_from_stock('tryton-delete', gtk.ICON_SIZE_MENU)
imagemenuitem_db_drop.set_image(image)
imagemenuitem_db_drop.connect('activate', self.sig_db_drop)
imagemenuitem_db_drop.set_accel_path(
'<tryton>/File/Database/Drop Database')
menu_database.add(imagemenuitem_db_drop)
imagemenuitem_close = gtk.ImageMenuItem(_('_Quit...'),
self.accel_group)
image = gtk.Image()
image.set_from_stock('tryton-log-out', gtk.ICON_SIZE_MENU)
imagemenuitem_close.set_image(image)
imagemenuitem_close.connect('activate', self.sig_close)
imagemenuitem_close.set_accel_path('<tryton>/File/Quit')
if self.macapp is None:
menu_file.add(gtk.SeparatorMenuItem())
menu_file.add(imagemenuitem_close)
return menu_file
def _set_menu_user(self):
menu_user = gtk.Menu()
imagemenuitem_preference = gtk.ImageMenuItem(_('_Preferences...'))
image = gtk.Image()
image.set_from_stock('tryton-preferences-system-session',
gtk.ICON_SIZE_MENU)
imagemenuitem_preference.set_image(image)
imagemenuitem_preference.connect('activate', self.sig_user_preferences)
imagemenuitem_preference.set_accel_path('<tryton>/User/Preferences')
menu_user.add(imagemenuitem_preference)
menu_user.add(gtk.SeparatorMenuItem())
imagemenuitem_menu = gtk.ImageMenuItem(_('_Menu Reload'),
self.accel_group)
image = gtk.Image()
image.set_from_stock('tryton-start-here', gtk.ICON_SIZE_MENU)
imagemenuitem_menu.set_image(image)
imagemenuitem_menu.connect('activate', lambda *a: self.sig_win_menu())
imagemenuitem_menu.set_accel_path('<tryton>/User/Menu Reload')
menu_user.add(imagemenuitem_menu)
imagemenuitem_menu_toggle = gtk.ImageMenuItem(_('_Menu Toggle'),
self.accel_group)
imagemenuitem_menu_toggle.connect('activate',
lambda *a: self.menu_toggle())
imagemenuitem_menu_toggle.set_accel_path('<tryton>/User/Menu Toggle')
menu_user.add(imagemenuitem_menu_toggle)
imagemenuitem_global_search = gtk.ImageMenuItem(_('_Global Search'),
self.accel_group)
image = gtk.Image()
image.set_from_stock('gtk-find', gtk.ICON_SIZE_MENU)
imagemenuitem_global_search.set_image(image)
imagemenuitem_global_search.connect('activate', lambda *a:
self.show_global_search())
imagemenuitem_global_search.set_accel_path(
'<tryton>/User/Global Search')
menu_user.add(imagemenuitem_global_search)
return menu_user
def _set_menu_options(self):
menu_options = gtk.Menu()
menuitem_toolbar = gtk.MenuItem(_('_Toolbar'))
menu_options.add(menuitem_toolbar)
menu_toolbar = gtk.Menu()
menu_toolbar.set_accel_group(self.accel_group)
menu_toolbar.set_accel_path('<tryton>/Options/Toolbar')
menuitem_toolbar.set_submenu(menu_toolbar)
radiomenuitem_default = gtk.RadioMenuItem(label=_('_Default'))
radiomenuitem_default.connect('activate',
lambda x: self.sig_toolbar('default'))
radiomenuitem_default.set_accel_path(
'<tryton>/Options/Toolbar/Default')
menu_toolbar.add(radiomenuitem_default)
if (CONFIG['client.toolbar'] or 'both') == 'default':
radiomenuitem_default.set_active(True)
radiomenuitem_both = gtk.RadioMenuItem(group=radiomenuitem_default,
label=_('_Text and Icons'))
radiomenuitem_both.connect('activate',
lambda x: self.sig_toolbar('both'))
radiomenuitem_both.set_accel_path(
'<tryton>/Options/Toolbar/Text and Icons')
menu_toolbar.add(radiomenuitem_both)
if (CONFIG['client.toolbar'] or 'both') == 'both':
radiomenuitem_both.set_active(True)
radiomenuitem_icons = gtk.RadioMenuItem(group=radiomenuitem_default,
label=_('_Icons'))
radiomenuitem_icons.connect('activate',
lambda x: self.sig_toolbar('icons'))
radiomenuitem_icons.set_accel_path('<tryton>/Options/Toolbar/Icons')
menu_toolbar.add(radiomenuitem_icons)
if (CONFIG['client.toolbar'] or 'both') == 'icons':
radiomenuitem_icons.set_active(True)
radiomenuitem_text = gtk.RadioMenuItem(group=radiomenuitem_default,
label=_('_Text'))
radiomenuitem_text.connect('activate',
lambda x: self.sig_toolbar('text'))
radiomenuitem_text.set_accel_path('<tryton>/Options/Toolbar/Text')
menu_toolbar.add(radiomenuitem_text)
if (CONFIG['client.toolbar'] or 'both') == 'text':
radiomenuitem_text.set_active(True)
# Menubar accelerators
menuitem_menubar = gtk.MenuItem(_('_Menubar'))
menu_options.add(menuitem_menubar)
menu_menubar = gtk.Menu()
menu_menubar.set_accel_group(self.accel_group)
menu_menubar.set_accel_path('<tryton>/Options/Menubar')
menuitem_menubar.set_submenu(menu_menubar)
checkmenuitem_accel = gtk.CheckMenuItem(_('Change Accelerators'))
checkmenuitem_accel.connect('activate',
lambda menuitem: self.sig_accel_change(menuitem.get_active()))
checkmenuitem_accel.set_accel_path('<tryton>/Options/Menubar/Accel')
menu_menubar.add(checkmenuitem_accel)
if CONFIG['client.can_change_accelerators']:
checkmenuitem_accel.set_active(True)
menuitem_mode = gtk.MenuItem(_('_Mode'))
menu_options.add(menuitem_mode)
menu_mode = gtk.Menu()
menu_mode.set_accel_group(self.accel_group)
menu_mode.set_accel_path('<tryton>/Options/Mode')
menuitem_mode.set_submenu(menu_mode)
radiomenuitem_normal = gtk.RadioMenuItem(label=_('_Normal'))
self.radiomenuitem_normal = radiomenuitem_normal
radiomenuitem_normal.connect('activate',
lambda x: self.sig_mode_change(False))
radiomenuitem_normal.set_accel_path('<tryton>/Options/Mode/Normal')
menu_mode.add(radiomenuitem_normal)
radiomenuitem_pda = gtk.RadioMenuItem(group=radiomenuitem_normal,
label=_('_PDA'))
self.radiomenuitem_pda = radiomenuitem_pda
radiomenuitem_pda.connect('activate',
lambda x: self.sig_mode_change(True))
radiomenuitem_pda.set_accel_path('<tryton>/Options/Mode/PDA')
menu_mode.add(radiomenuitem_pda)
menuitem_form = gtk.MenuItem(_('_Form'))
menu_options.add(menuitem_form)
menu_form = gtk.Menu()
menu_form.set_accel_group(self.accel_group)
menu_form.set_accel_path('<tryton>/Options/Form')
menuitem_form.set_submenu(menu_form)
checkmenuitem_save_width_height = gtk.CheckMenuItem(
_('Save Width/Height'))
checkmenuitem_save_width_height.connect('activate',
lambda menuitem: CONFIG.__setitem__('client.save_width_height',
menuitem.get_active()))
checkmenuitem_save_width_height.set_accel_path(
'<tryton>/Options/Form/Save Width Height')
menu_form.add(checkmenuitem_save_width_height)
if CONFIG['client.save_width_height']:
checkmenuitem_save_width_height.set_active(True)
checkmenuitem_save_tree_state = gtk.CheckMenuItem(
_('Save Tree State'))
checkmenuitem_save_tree_state.connect('activate',
lambda menuitem: CONFIG.__setitem__(
'client.save_tree_state',
menuitem.get_active()))
checkmenuitem_save_tree_state.set_accel_path(
'<tryton>/Options/Form/Save Tree State')
menu_form.add(checkmenuitem_save_tree_state)
if CONFIG['client.save_tree_state']:
checkmenuitem_save_tree_state.set_active(True)
if gtkspell:
checkmenuitem_spellcheck = gtk.CheckMenuItem(_('Spell Checking'))
checkmenuitem_spellcheck.connect('activate',
lambda menuitem: CONFIG.__setitem__('client.spellcheck',
menuitem.get_active()))
checkmenuitem_spellcheck.set_accel_path(
'<tryton>/Options/Form/Spell Checking')
menu_form.add(checkmenuitem_spellcheck)
if CONFIG['client.spellcheck']:
checkmenuitem_spellcheck.set_active(True)
menuitem_tab = gtk.MenuItem(_('Tabs Position'))
menu_form.add(menuitem_tab)
menu_tab = gtk.Menu()
menu_tab.set_accel_group(self.accel_group)
menu_tab.set_accel_path('<tryton>/Options/Tabs Position')
menuitem_tab.set_submenu(menu_tab)
radiomenuitem_top = gtk.RadioMenuItem(label=_('Top'))
radiomenuitem_top.connect('activate',
lambda x: CONFIG.__setitem__('client.form_tab', 'top'))
radiomenuitem_top.set_accel_path('<tryton>/Options/Tabs Position/Top')
menu_tab.add(radiomenuitem_top)
if (CONFIG['client.form_tab'] or 'left') == 'top':
radiomenuitem_top.set_active(True)
radiomenuitem_left = gtk.RadioMenuItem(group=radiomenuitem_top,
label=_('Left'))
radiomenuitem_left.connect('activate',
lambda x: CONFIG.__setitem__('client.form_tab', 'left'))
radiomenuitem_left.set_accel_path(
'<tryton>/Options/Tabs Position/Left')
menu_tab.add(radiomenuitem_left)
if (CONFIG['client.form_tab'] or 'left') == 'left':
radiomenuitem_left.set_active(True)
radiomenuitem_right = gtk.RadioMenuItem(group=radiomenuitem_top,
label=_('Right'))
radiomenuitem_right.connect('activate',
lambda x: CONFIG.__setitem__('client.form_tab', 'right'))
radiomenuitem_right.set_accel_path(
'<tryton>/Options/Tabs Position/Right')
menu_tab.add(radiomenuitem_right)
if (CONFIG['client.form_tab'] or 'left') == 'right':
radiomenuitem_right.set_active(True)
radiomenuitem_bottom = gtk.RadioMenuItem(group=radiomenuitem_top,
label=_('Bottom'))
radiomenuitem_bottom.connect('activate',
lambda x: CONFIG.__setitem__('client.form_tab', 'bottom'))
radiomenuitem_bottom.set_accel_path(
'<tryton>/Options/Tabs Position/Bottom')
menu_tab.add(radiomenuitem_bottom)
if (CONFIG['client.form_tab'] or 'left') == 'bottom':
radiomenuitem_bottom.set_active(True)
imagemenuitem_win_prev = gtk.ImageMenuItem(_('_Previous Tab'),
self.accel_group)
imagemenuitem_win_prev.connect('activate', self.sig_win_prev)
imagemenuitem_win_prev.set_accel_path('<tryton>/Form/Previous Tab')
menu_form.add(imagemenuitem_win_prev)
imagemenuitem_win_next = gtk.ImageMenuItem(_('_Next Tab'),
self.accel_group)
imagemenuitem_win_next.connect('activate', self.sig_win_next)
imagemenuitem_win_next.set_accel_path('<tryton>/Form/Next Tab')
menu_form.add(imagemenuitem_win_next)
menuitem_limit = gtk.MenuItem(_('Search Limit...'))
self.menuitem_limit = menuitem_limit
menuitem_limit.connect('activate', self.sig_limit)
menuitem_limit.set_accel_path('<tryton>/Options/Search Limit')
menu_options.add(menuitem_limit)
menuitem_email = gtk.MenuItem(_('_Email...'))
self.menuitem_email = menuitem_email
menuitem_email.connect('activate', self.sig_email)
menuitem_email.set_accel_path('<tryton>/Options/Email')
menu_options.add(menuitem_email)
menu_options.add(gtk.SeparatorMenuItem())
imagemenuitem_opt_save = gtk.ImageMenuItem(_('_Save Options'))
image = gtk.Image()
image.set_from_stock('tryton-save', gtk.ICON_SIZE_MENU)
imagemenuitem_opt_save.set_image(image)
imagemenuitem_opt_save.connect('activate', lambda x: CONFIG.save())
imagemenuitem_opt_save.set_accel_path('<tryton>/Options/Save Options')
menu_options.add(imagemenuitem_opt_save)
return menu_options
def _set_menu_help(self):
menu_help = gtk.Menu()
imagemenuitem_tips = gtk.ImageMenuItem(_('_Tips...'))
image = gtk.Image()
image.set_from_stock('tryton-information', gtk.ICON_SIZE_MENU)
imagemenuitem_tips.set_image(image)
imagemenuitem_tips.connect('activate', self.sig_tips)
imagemenuitem_tips.set_accel_path('<tryton>/Help/Tips')
menu_help.add(imagemenuitem_tips)
imagemenuitem_shortcuts = gtk.ImageMenuItem(
_('_Keyboard Shortcuts...'))
image = gtk.Image()
image.set_from_stock('tryton-help', gtk.ICON_SIZE_MENU)
imagemenuitem_shortcuts.set_image(image)
imagemenuitem_shortcuts.connect('activate', self.sig_shortcuts)
imagemenuitem_shortcuts.set_accel_path(
'<tryton>/Help/Keyboard Shortcuts')
menu_help.add(imagemenuitem_shortcuts)
imagemenuitem_about = gtk.ImageMenuItem(_('_About...'))
image = gtk.Image()
image.set_from_stock('gtk-about', gtk.ICON_SIZE_MENU)
imagemenuitem_about.set_image(image)
imagemenuitem_about.connect('activate', self.sig_about)
imagemenuitem_about.set_accel_path('<tryton>/Help/About')
self.aboutitem = imagemenuitem_about
if self.macapp is None:
menu_help.add(gtk.SeparatorMenuItem())
menu_help.add(imagemenuitem_about)
return menu_help
@staticmethod
def get_main():
return _MAIN[0]
def favorite_set(self):
if not self.menu_screen:
return False
def _action_favorite(widget, id_):
Action.exec_keyword('tree_open', {
'model': self.menu_screen.model_name,
'id': id_,
'ids': [id_],
})
def _manage_favorites(widget):
Window.create(False, self.menu_screen.model_name + '.favorite',
False, mode=['tree', 'form'])
try:
favorites = RPCExecute('model',
self.menu_screen.model_name + '.favorite', 'get')
except RPCException:
favorites = []
menu = self.menuitem_favorite.get_submenu()
if not menu:
menu = gtk.Menu()
for id_, name, icon in favorites:
if icon:
common.ICONFACTORY.register_icon(icon)
menuitem = gtk.ImageMenuItem(name)
image = gtk.Image()
image.set_from_stock(icon, gtk.ICON_SIZE_MENU)
menuitem.set_image(image)
else:
menuitem = gtk.MenuItem(name)
menuitem.connect('activate', _action_favorite, id_)
menu.add(menuitem)
menu.add(gtk.SeparatorMenuItem())
manage_favorites = gtk.MenuItem(_('Manage Favorites'))
manage_favorites.connect('activate', _manage_favorites)
menu.add(manage_favorites)
menu.show_all()
self.menuitem_favorite.set_submenu(menu)
return True
def favorite_unset(self):
had_submenu = self.menuitem_favorite.get_submenu()
self.menuitem_favorite.remove_submenu()
# Set a submenu to get keyboard shortcut working
self.menuitem_favorite.set_submenu(gtk.Menu())
if self.macapp and had_submenu:
# As the select event is not managed by the mac menu,
# it is done using a timeout
gobject.timeout_add(1000, lambda: not self.favorite_set())
def sig_accel_change(self, value):
CONFIG['client.can_change_accelerators'] = value
return self.sig_accel()
def sig_accel(self):
menubar = CONFIG['client.can_change_accelerators']
settings = gtk.settings_get_default()
if menubar:
settings.set_property('gtk-can-change-accels', True)
else:
settings.set_property('gtk-can-change-accels', False)
def sig_mode_change(self, pda_mode=False):
CONFIG['client.modepda'] = pda_mode
return
def sig_toolbar(self, option):
CONFIG['client.toolbar'] = option
if option == 'default':
barstyle = False
elif option == 'both':
barstyle = gtk.TOOLBAR_BOTH
elif option == 'text':
barstyle = gtk.TOOLBAR_TEXT
elif option == 'icons':
barstyle = gtk.TOOLBAR_ICONS
for page_idx in range(self.notebook.get_n_pages()):
page = self.get_page(page_idx)
page.toolbar.set_style(barstyle)
def sig_limit(self, widget):
Limit().run()
def sig_email(self, widget):
Email().run()
def sig_win_next(self, widget):
page = self.notebook.get_current_page()
if page == len(self.pages) - 1:
page = -1
self.notebook.set_current_page(page + 1)
def sig_win_prev(self, widget):
page = self.notebook.get_current_page()
self.notebook.set_current_page(page - 1)
def get_preferences(self):
def _set_preferences(prefs):
try:
prefs = prefs()
except RPCException:
prefs = {}
threads = []
for target in (
common.ICONFACTORY.load_icons,
common.MODELACCESS.load_models,
common.MODELHISTORY.load_history,
common.VIEW_SEARCH.load_searches,
):
t = threading.Thread(target=target)
threads.append(t)
t.start()
for t in threads:
t.join()
if prefs and 'language_direction' in prefs:
translate.set_language_direction(prefs['language_direction'])
CONFIG['client.language_direction'] = \
prefs['language_direction']
self.sig_win_menu(prefs=prefs)
for action_id in prefs.get('actions', []):
Action.execute(action_id, {})
self.set_title(prefs.get('status_bar', ''))
if prefs and 'language' in prefs:
translate.setlang(prefs['language'], prefs.get('locale'))
if CONFIG['client.lang'] != prefs['language']:
self.set_menubar()
self.favorite_unset()
CONFIG['client.lang'] = prefs['language']
CONFIG.save()
def _get_preferences():
RPCExecute('model', 'res.user', 'get_preferences', False,
callback=_set_preferences)
RPCContextReload(_get_preferences)
def sig_user_preferences(self, widget):
if not self.close_pages():
return False
Preference(rpc._USER, self.get_preferences)
def sig_win_close(self, widget=None):
self._sig_remove_book(widget,
self.notebook.get_nth_page(self.notebook.get_current_page()))
def sig_login(self, widget=None, res=None):
if not self.sig_logout(widget, disconnect=False):
return
if not res:
try:
res = DBLogin().run()
except TrytonError, exception:
if exception.faultCode == 'QueryCanceled':
return False
except TrytonServerError, exception:
common.process_exception(exception)
return
try:
log_response = rpc.login(*res)
except TrytonServerError, exception:
common.process_exception(exception)
return
if log_response > 0:
self.get_preferences()
elif log_response == -1:
common.message(_('Connection error!\n'
'Unable to connect to the server!'))
elif log_response == -2:
common.message(_('Connection error!\n'
'Bad username or password!'))
return self.sig_login()
self.favorite_unset()
self.menuitem_favorite.set_sensitive(True)
self.menuitem_user.set_sensitive(True)
if CONFIG.arguments:
url = CONFIG.arguments.pop()
self.open_url(url)
return True
def close_pages(self):
if self.notebook.get_n_pages():
if not common.sur(
_('The following action requires to close all tabs.\n'
'Do you want to continue?')):
return False
res = True
while res:
wid = self.get_page()
if wid:
if not wid.sig_close():
return False
res = self._win_del()
else:
res = False
if self.menu_screen:
self.menu_screen.save_tree_state()
self.menu_screen.destroy()
self.menu_screen = None
self.menu_expander_clear()
return True
def sig_logout(self, widget=None, disconnect=True):
try:
if not self.close_pages():
return False
except TrytonServerUnavailable:
pass
self.set_title()
self.favorite_unset()
self.menuitem_favorite.set_sensitive(False)
self.menuitem_user.set_sensitive(False)
if disconnect:
rpc.logout()
return True
def sig_tips(self, *args):
Tips()
def sig_about(self, widget):
About()
def sig_shortcuts(self, widget):
Shortcuts().run()
def menu_toggle(self):
expander = self.pane.get_child1()
if expander:
expander.set_expanded(not expander.get_expanded())
@property
def menu_expander_size(self):
return self.menu_expander.style_get_property('expander-size')
def menu_expanded(self, expander, *args):
expanded = expander.get_expanded()
CONFIG['menu.expanded'] = expanded
if expanded:
self.pane.set_position(int(CONFIG['menu.pane']))
if self.menu_screen:
self.menu_screen.set_cursor()
else:
CONFIG['menu.pane'] = self.pane.get_position()
self.pane.set_position(self.menu_expander_size)
self.notebook.grab_focus()
def menu_expander_clear(self):
self.menu_expander.hide()
if self.menu_expander.get_child():
self.menu_expander.remove(self.menu_expander.get_child())
expanded = self.menu_expander.get_expanded()
CONFIG['menu.expanded'] = expanded
if expanded:
CONFIG['menu.pane'] = self.pane.get_position()
def on_paned_button_press_event(self, paned, event):
expander = self.pane.get_child1()
if expander:
return not expander.get_expanded()
return False
def sig_win_menu(self, prefs=None):
from tryton.gui.window.view_form.screen import Screen
if not prefs:
try:
prefs = RPCExecute('model', 'res.user', 'get_preferences',
False)
except RPCException:
return False
vbox = gtk.VBox()
self.set_global_search()
vbox.pack_start(self.global_search_entry, False, False)
vbox.show_all()
if self.menu_screen:
self.menu_screen.save_tree_state()
self.menu_screen = None
self.menu_expander_clear()
action = PYSONDecoder().decode(prefs['pyson_menu'])
view_ids = False
if action.get('views', []):
view_ids = [x[0] for x in action['views']]
elif action.get('view_id', False):
view_ids = [action['view_id'][0]]
ctx = rpc.CONTEXT.copy()
decoder = PYSONDecoder(ctx)
action_ctx = decoder.decode(action.get('pyson_context') or '{}')
domain = decoder.decode(action['pyson_domain'])
screen = Screen(action['res_model'], mode=['tree'], view_ids=view_ids,
domain=domain, context=action_ctx, readonly=True)
# Use alternate view to not show search box
screen.screen_container.alternate_view = True
screen.switch_view(view_type=screen.current_view.view_type)
vbox.pack_start(screen.screen_container.alternate_viewport, True, True)
treeview = screen.current_view.treeview
treeview.set_headers_visible(False)
self.menu_expander.add(vbox)
self.menu_expander.show()
# Favorite column
column = gtk.TreeViewColumn()
column.name = None
column._type = None
favorite_renderer = CellRendererClickablePixbuf()
column.pack_start(favorite_renderer, expand=False)
def favorite_setter(column, cell, store, iter_):
menu = store.get_value(iter_, 0)
favorite = menu.value.get('favorite')
if favorite:
stock_id = 'tryton-star'
elif favorite is False:
stock_id = 'tryton-unstar'
else:
stock_id = ''
pixbuf = treeview.render_icon(stock_id=stock_id,
size=gtk.ICON_SIZE_MENU, detail=None)
cell.set_property('pixbuf', pixbuf)
column.set_cell_data_func(favorite_renderer, favorite_setter)
def toggle_favorite(renderer, path, treeview):
if treeview.props.window:
self.toggle_favorite(renderer, path, treeview)
favorite_renderer.connect('clicked',
lambda *a: gobject.idle_add(toggle_favorite, *a), treeview)
# Unset fixed height mode to add column
treeview.set_fixed_height_mode(False)
treeview.append_column(column)
screen.search_filter()
screen.display(set_cursor=True)
self.menu_screen = screen
def toggle_favorite(self, renderer, path, treeview):
store = treeview.get_model()
iter_ = store.get_iter(path)
menu = store.get_value(iter_, 0)
favorite = menu.value.get('favorite')
if favorite:
value = False
method = 'unset'
elif favorite is False:
value = True
method = 'set'
else:
return
try:
RPCExecute('model', self.menu_screen.model_name + '.favorite',
method, menu.id)
except RPCException:
return
menu.value['favorite'] = value
store.emit('row-changed', path, iter_)
self.favorite_unset()
@classmethod
def sig_quit(cls, widget=None):
rpc.logout()
CONFIG['client.default_width'] = Main.get_main()._width
CONFIG['client.default_height'] = Main.get_main()._height
CONFIG.save()
gtk.accel_map_save(os.path.join(get_config_dir(), 'accel.map'))
cls.tryton_client.quit_mainloop()
sys.exit()
def sig_close(self, widget, event=None):
if not self.sig_logout(widget):
return True
Main.sig_quit()
def sig_configure(self, widget, event):
if hasattr(event, 'width') \
and hasattr(event, 'height'):
self._width = int(event.width)
self._height = int(event.height)
return False
def sig_window_state(self, widget, event):
CONFIG['client.maximize'] = (event.new_window_state ==
gtk.gdk.WINDOW_STATE_MAXIMIZED)
return False
def win_add(self, page, hide_current=False, allow_similar=True):
if not allow_similar:
for other_page in self.pages:
if page == other_page:
current_page = self.notebook.get_current_page()
page_num = self.notebook.page_num(other_page.widget)
other_page.widget.props.visible = True
self.notebook.set_current_page(page_num)
# In order to focus the page
if current_page == page_num:
self._sig_page_changt(self.notebook, None, page_num)
return
previous_page_id = self.notebook.get_current_page()
previous_widget = self.notebook.get_nth_page(previous_page_id)
if previous_widget and hide_current:
prev_tab_label = self.notebook.get_tab_label(previous_widget)
prev_tab_label.set_size_request(TAB_SIZE / 4, -1)
close_button = prev_tab_label.get_children()[-1]
close_button.hide()
page_id = previous_page_id + 1
else:
page_id = -1
self.previous_pages[page] = previous_widget
self.pages.append(page)
hbox = gtk.HBox(spacing=3)
icon_w, icon_h = gtk.icon_size_lookup(gtk.ICON_SIZE_SMALL_TOOLBAR)
if page.icon is not None:
common.ICONFACTORY.register_icon(page.icon)
image = gtk.Image()
image.set_from_stock(page.icon, gtk.ICON_SIZE_SMALL_TOOLBAR)
hbox.pack_start(image, expand=False, fill=False)
noise_size = 2 * icon_w + 3
else:
noise_size = icon_w + 3
name = page.name
label = gtk.Label(name)
self.tooltips.set_tip(label, page.name)
self.tooltips.enable()
label.set_alignment(0.0, 0.5)
hbox.pack_start(label, expand=True, fill=True)
layout = label.get_layout()
w, h = layout.get_size()
if (w // pango.SCALE) > TAB_SIZE - noise_size:
label2 = gtk.Label('...')
self.tooltips.set_tip(label2, page.name)
hbox.pack_start(label2, expand=False, fill=False)
button = gtk.Button()
img = gtk.Image()
img.set_from_stock('tryton-close', gtk.ICON_SIZE_MENU)
width, height = img.size_request()
button.set_relief(gtk.RELIEF_NONE)
button.unset_flags(gtk.CAN_FOCUS)
button.add(img)
self.tooltips.set_tip(button, _('Close Tab'))
button.connect('clicked', self._sig_remove_book, page.widget)
hbox.pack_start(button, expand=False, fill=False)
x, y = gtk.icon_size_lookup_for_settings(button.get_settings(),
gtk.ICON_SIZE_MENU)
button.set_size_request(x, y)
hbox.show_all()
hbox.set_size_request(TAB_SIZE, -1)
label_menu = gtk.Label(page.name)
label_menu.set_alignment(0.0, 0.5)
self.notebook.insert_page_menu(page.widget, hbox, label_menu, page_id)
self.notebook.set_tab_reorderable(page.widget, True)
self.notebook.set_current_page(page_id)
def _sig_remove_book(self, widget, page_widget):
for page in self.pages:
if page.widget == page_widget:
if not page.widget.props.sensitive:
return
page_num = self.notebook.page_num(page.widget)
self.notebook.set_current_page(page_num)
res = page.sig_close()
if not res:
return
self._win_del(page_widget)
def _win_del(self, page_widget=None):
if page_widget:
page_id = self.notebook.page_num(page_widget)
else:
page_id = int(self.notebook.get_current_page())
page_widget = self.notebook.get_nth_page(page_id)
if page_id != -1:
page = None
for i in range(len(self.pages)):
if self.pages[i].widget == page_widget:
page = self.pages.pop(i)
page.signal_unconnect(self)
break
self.notebook.remove_page(page_id)
next_page_id = -1
to_pop = []
for i in self.previous_pages:
if self.previous_pages[i] == page_widget:
to_pop.append(i)
if i.widget == page_widget:
if self.previous_pages[i]:
next_page_id = self.notebook.page_num(
self.previous_pages[i])
to_pop.append(i)
to_pop.reverse()
for i in to_pop:
self.previous_pages.pop(i)
if hasattr(page, 'destroy'):
page.destroy()
del page
current_widget = self.notebook.get_nth_page(next_page_id)
if current_widget:
current_widget.props.visible = True
self.notebook.set_current_page(next_page_id)
if not self.pages and self.menu_screen:
self.menu_screen.set_cursor()
return self.notebook.get_current_page() != -1
def get_page(self, page_id=None):
if page_id is None:
page_id = self.notebook.get_current_page()
if page_id == -1:
return None
page_widget = self.notebook.get_nth_page(page_id)
for page in self.pages:
if page.widget == page_widget:
return page
return None
def _sig_page_changt(self, notebook, page, page_num):
self.last_page = self.current_page
last_form = self.get_page(self.current_page)
tab_label = notebook.get_tab_label(notebook.get_nth_page(page_num))
tab_label.set_size_request(TAB_SIZE, -1)
close_button = tab_label.get_children()[-1]
close_button.show()
if last_form:
for dialog in last_form.dialogs:
dialog.hide()
self.current_page = self.notebook.get_current_page()
current_form = self.get_page(self.current_page)
def set_cursor():
if self.current_page == self.notebook.get_current_page():
current_form.set_cursor()
# Using idle_add because the gtk.TreeView grabs the focus at the
# end of the event
gobject.idle_add(set_cursor)
for dialog in current_form.dialogs:
dialog.show()
def sig_db_new(self, widget):
if not self.sig_logout(widget):
return False
dia = DBCreate(CONFIG['login.server'], int(CONFIG['login.port']),
sig_login=self.sig_login)
res = dia.run()
if res:
CONFIG.save()
return res
def sig_db_drop(self, widget=None):
if not self.sig_logout(widget):
return False
url, dbname, passwd = DBBackupDrop(function='drop').run()
if not dbname:
rpc.logout()
return
host, port = url.rsplit(':', 1)
sure = common.sur_3b(_("You are going to delete a Tryton "
"database.\nAre you really sure to proceed?"))
if sure == "ko" or sure == "cancel":
return
rpcprogress = common.RPCProgress('db_exec', (host, int(port), 'drop',
dbname, passwd))
try:
rpcprogress.run(False)
except TrytonServerError, exception:
if exception.faultCode == "AccessDenied":
common.warning(_("Wrong Tryton Server Password\n"
"Please try again."),
_('Access denied!'))
self.sig_db_drop()
else:
common.warning(_('Database drop failed with error message:\n')
+ str(exception.faultCode), _('Database drop failed!'))
return
common.message(_("Database dropped successfully!"))
def sig_db_restore(self, widget=None):
if not self.sig_logout(widget):
return False
filename = common.file_selection(_('Open Backup File to Restore...'),
preview=False)
if not filename:
return
dialog = DBRestore(filename=filename)
url, dbname, passwd, update = dialog.run()
if dbname:
with open(filename, 'rb') as file_p:
data = file_p.read()
host, port = url.rsplit(':', 1)
rpcprogress = common.RPCProgress('db_exec', (host, int(port),
'restore', dbname, passwd, buffer(data), update))
try:
res = rpcprogress.run(False)
except TrytonServerError, exception:
if exception.faultCode == \
"Couldn't restore database with password":
common.warning(_("It is not possible to restore a "
"password protected database.\n"
"Backup and restore needed to be proceed "
"manual."),
_('Database is password protected!'))
elif exception.faultCode == "AccessDenied":
common.warning(_("Wrong Tryton Server Password.\n"
"Please try again."),
_('Access denied!'))
self.sig_db_restore()
else:
common.warning(_('Database restore failed with '
'error message:\n') + str(exception.faultCode),
_('Database restore failed!'))
return
if res:
common.message(_("Database restored successfully!"))
else:
common.message(_('Database restore failed!'))
def sig_db_dump(self, widget=None):
if not self.sig_logout(widget):
return False
dialog = DBBackupDrop(function='backup')
url, dbname, passwd = dialog.run()
if not (dbname and url and passwd):
rpc.logout()
return
host, port = url.rsplit(':', 1)
rpcprogress = common.RPCProgress('db_exec', (host, int(port), 'dump',
dbname, passwd))
try:
dump = rpcprogress.run(False)
except TrytonServerError, exception:
if exception.faultCode == "Couldn't dump database with password":
common.warning(_("It is not possible to dump a password "
"protected Database.\nBackup and restore "
"needed to be proceed manual."),
_('Database is password protected!'))
elif exception.faultCode == "AccessDenied":
common.warning(_("Wrong Tryton Server Password.\n"
"Please try again."),
_('Access denied!'))
self.sig_db_dump()
else:
common.warning(_('Database dump failed with '
'error message:\n') + str(exception.faultCode),
_('Database dump failed!'))
rpc.logout()
return
filename = common.file_selection(_('Save As...'),
action=gtk.FILE_CHOOSER_ACTION_SAVE, preview=False,
filename=dbname + '-' + time.strftime('%Y%m%d%H%M') + '.dump')
if filename:
with open(filename, 'wb') as file_:
file_.write(dump)
common.message(_("Database backuped successfully!"))
else:
rpc.logout()
def _open_url(self, url):
urlp = urlparse(url)
if not urlp.scheme == 'tryton':
return
urlp = urlparse('http' + url[6:])
hostname, port = map(urllib.unquote,
(urlp.netloc.split(':', 1) + [CONFIG.defaults['login.port']])[:2])
database, path = map(urllib.unquote,
(urlp.path[1:].split('/', 1) + [''])[:2])
if (not path or
hostname != rpc._HOST or
int(port) != rpc._PORT or
database != rpc._DATABASE):
return
type_, path = (path.split('/', 1) + [''])[:2]
params = {}
if urlp.params:
try:
params.update(dict(parse_qsl(urlp.params,
strict_parsing=True)))
except ValueError:
return
def open_model(path):
model, path = (path.split('/', 1) + [''])[:2]
if not model:
return
res_id = False
mode = None
try:
view_ids = json.loads(params.get('views', 'false'))
limit = json.loads(params.get('limit', 'null'))
name = json.loads(params.get('window_name', 'false'))
search_value = json.loads(params.get('search_value', '{}'),
object_hook=object_hook)
domain = json.loads(params.get('domain', '[]'),
object_hook=object_hook)
context = json.loads(params.get('context', '{}'),
object_hook=object_hook)
except ValueError:
return
if path:
try:
res_id = int(path)
except ValueError:
return
mode = ['form', 'tree']
try:
Window.create(view_ids, model, res_id=res_id, domain=domain,
context=context, mode=mode, name=name, limit=limit,
search_value=search_value)
except Exception:
# Prevent crashing the client
return
def open_wizard(wizard):
if not wizard:
return
try:
data = json.loads(params.get('data', '{}'),
object_hook=object_hook)
direct_print = json.loads(params.get('direct_print', 'false'))
email_print = json.loads(params.get('email_print', 'false'))
email = json.loads(params.get('email', 'null'))
name = json.loads(params.get('name', 'false'))
window = json.loads(params.get('window', 'false'))
context = json.loads(params.get('context', '{}'),
object_hook=object_hook)
except ValueError:
return
try:
Window.create_wizard(wizard, data, direct_print=direct_print,
email_print=email_print, email=email, name=name,
context=context, window=window)
except Exception:
# Prevent crashing the client
return
def open_report(report):
if not report:
return
try:
data = json.loads(params.get('data'), object_hook=object_hook)
direct_print = json.loads(params.get('direct_print', 'false'))
email_print = json.loads(params.get('email_print', 'false'))
email = json.loads(params.get('email', 'null'))
context = json.loads(params.get('context', '{}'),
object_hook=object_hook)
except ValueError:
return
try:
Action.exec_report(report, data, direct_print=direct_print,
email_print=email_print, email=email, context=context)
except Exception:
# Prevent crashing the client
return
def open_url():
try:
url = json.loads(params.get('url', 'false'))
except ValueError:
return
if url:
webbrowser.open(url, new=2)
if type_ == 'model':
open_model(path)
elif type_ == 'wizard':
open_wizard(path)
elif type_ == 'report':
open_report(path)
elif type_ == 'url':
open_url()
self.window.present()
def open_url(self, url):
def idle_open_url():
with gtk.gdk.lock:
self._open_url(url)
return False
gobject.idle_add(idle_open_url)