def _sig_edit(self): if not self.screen.current_record: return # Create a new screen that is not linked to the parent otherwise on the # save of the record will trigger the save of the parent domain = self.field.domain_get(self.record) add_remove = self.record.expr_eval(self.attrs.get('add_remove')) if add_remove: domain = [domain, add_remove] context = self.field.context_get(self.record) screen = Screen(self.attrs['relation'], domain=domain, view_ids=self.attrs.get('view_ids', '').split(','), mode=['form'], views_preload=self.attrs.get('views', {}), readonly=self.attrs.get('readonly', False), context=context) screen.load([self.screen.current_record.id]) def callback(result): if result: screen.current_record.save() # Force a reload on next display self.screen.current_record.cancel() # Force a display to clear the CellCache self.screen.display() WinForm(screen, callback)
def __init__(self, record, callback=None): self.resource = '%s,%s' % (record.model_name, record.id) self.note_callback = callback context = record.context_get() context['resource'] = self.resource screen = Screen('ir.note', domain=[ ('resource', '=', self.resource), ], mode=['tree', 'form'], context=context, exclude_field='resource') super(Note, self).__init__(screen, self.callback, view_type='tree') screen.search_filter()
def __init__(self, model_name, record_id, callback=None): self.resource = '%s,%s' % (model_name, record_id) self.attachment_callback = callback screen = Screen('ir.attachment', domain=[ ('resource', '=', self.resource), ], mode=['tree', 'form'], context={ 'resource': self.resource, }, exclude_field='resource') screen.search_filter() screen.parent = True super(Attachment, self).__init__(screen, self.callback, view_type='tree')
def open_remote(self, record, create=True, changed=False, text=None, callback=None): field = record.group.fields[self.field_name] relation = field.attrs['relation'] domain = field.domain_get(record) context = field.context_get(record) if create: obj_id = None elif not changed: obj_id = field.get(record) else: if text: dom = [('rec_name', 'ilike', '%' + text + '%'), domain] else: dom = domain try: ids = RPCExecute('model', relation, 'search', dom, 0, None, None, context=context) except RPCException: field.set_client(record, False) if callback: callback() return if len(ids) == 1: field.set_client(record, ids[0]) if callback: callback() return self.search_remote(record, relation, ids, domain=domain, context=context, callback=callback) return screen = Screen(relation, domain=domain, context=context, mode=['form']) def open_callback(result): if result and screen.save_current(): value = (screen.current_record.id, screen.current_record.rec_name()) field.set_client(record, value, force_change=True) elif result: screen.display() return WinForm(screen, open_callback) if callback: callback() if obj_id: screen.load([obj_id]) WinForm(screen, open_callback) else: WinForm(screen, open_callback, new=True)
def open_remote(self, record, create=True, changed=False, text=None, callback=None): group = record.value[self.field_name] field = record.group.fields[self.field_name] relation = field.attrs['relation'] context = field.context_get(record) screen = Screen(relation, mode=['tree', 'form'], exclude_field=field.attrs.get('relation_field')) screen.group = group def open_callback(result): if callback: callback() WinForm(screen, open_callback, view_type='tree', context=context)
def __init__(self, view, attrs): super(Many2ManySelection, self).__init__(view, attrs) self.widget = gtk.VBox(homogeneous=False, spacing=5) hbox = gtk.HBox(homogeneous=False, spacing=0) hbox.set_border_width(2) label = gtk.Label(attrs.get("string", "")) label.set_alignment(0.0, 0.5) hbox.pack_start(label, expand=True, fill=True) frame = gtk.Frame() frame.add(hbox) frame.set_shadow_type(gtk.SHADOW_OUT) self.widget.pack_start(frame, expand=False, fill=True) self.screen = Screen( attrs["relation"], view_ids=attrs.get("view_ids", "").split(","), mode=["tree"], views_preload=attrs.get("views", {}), ) self.screen.new_group() self.treeview = self.screen.current_view.treeview self.treeview.get_selection().connect("changed", self.changed) self.treeview.connect("focus-out-event", lambda *a: self._focus_out()) self.treeview.connect("button-press-event", self.button_press_event) self.treeview.connect("key-press-event", self.key_press_event) self.widget.pack_start(self.screen.widget, expand=True, fill=True) self.nullable_widget = False self.init_selection()
def __init__(self, user, callback): NoModal.__init__(self) self.callback = callback self.win = gtk.Dialog(_("Preferences"), self.parent, gtk.DIALOG_DESTROY_WITH_PARENT) self.win.set_position(gtk.WIN_POS_CENTER_ON_PARENT) self.win.set_has_separator(False) self.win.set_icon(TRYTON_ICON) self.accel_group = gtk.AccelGroup() self.win.add_accel_group(self.accel_group) self.but_cancel = self.win.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL) self.but_ok = self.win.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK) self.but_ok.add_accelerator( "clicked", self.accel_group, gtk.keysyms.Return, gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE ) self.win.set_default_response(gtk.RESPONSE_OK) self.win.connect("response", self.response) try: view = RPCExecute("model", "res.user", "get_preferences_fields_view") except RPCException: self.win.destroy() self.win = None return title = gtk.Label(_("Edit User Preferences")) title.show() self.win.vbox.pack_start(title, expand=False, fill=True) self.screen = Screen("res.user", mode=[]) # Reset readonly set automaticly by MODELACCESS self.screen.readonly = False self.screen.group.readonly = False self.screen.group.skip_model_access = True self.screen.add_view(view) self.screen.switch_view() self.screen.new(default=False) try: preferences = RPCExecute("model", "res.user", "get_preferences", False) except RPCException: self.win.destroy() self.win = None return self.screen.current_record.set(preferences) self.screen.current_record.id = rpc._USER self.screen.current_record.validate(softvalidation=True) self.screen.display(set_cursor=True) self.screen.widget.show() self.win.vbox.pack_start(self.screen.widget) self.win.set_title(_("Preference")) width, height = self.parent.get_size() self.win.set_default_size(int(width * 0.95), int(height * 0.95)) self.register() self.win.show()
def open_remote(self, record, create=True, changed=False, text=None, callback=None): group = record.value[self.attrs['name']] field = record.group.fields[self.attrs['name']] relation = field.attrs['relation'] context = field.get_context(record) domain = field.domain_get(record) screen = Screen(relation, mode=['tree', 'form'], view_ids=self.attrs.get('view_ids', '').split(','), exclude_field=field.attrs.get('relation_field')) screen.group = group def open_callback(result): if callback: callback() WinForm(screen, open_callback, view_type='tree', domain=domain, context=context, title=field.attrs.get('string'))
def get_screen(self): domain = self.field.domain_get(self.record) context = self.field.get_context(self.record) # Remove first tree view as mode is form only view_ids = self.attrs.get('view_ids', '').split(',')[1:] return Screen(self.get_model(), domain=domain, context=context, mode=['form'], view_ids=view_ids, views_preload=self.attrs.get('views', {}), readonly=self._readonly, exclude_field=self.attrs.get('relation_field'))
def __init__(self, model, res_id=None, name='', **attributes): super(Form, self).__init__(**attributes) self.model = model self.res_id = res_id self.mode = attributes.get('mode') self.view_ids = attributes.get('view_ids') self.dialogs = [] if not name: name = common.MODELNAME.get(model) self.name = name self.screen = Screen(self.model, breadcrumb=[self.name], **attributes) self.screen.widget.show() self.create_tabcontent() self.set_buttons_sensitive() self.screen.signal_connect(self, 'record-message', self._record_message) self.screen.signal_connect(self, 'record-modified', lambda *a: GLib.idle_add(self._record_modified, *a)) self.screen.signal_connect(self, 'record-saved', self._record_saved) self.screen.signal_connect( self, 'resources', lambda screen, resources: self.update_resources(resources)) self.attachment_screen = None if res_id not in (None, False): if isinstance(res_id, int): res_id = [res_id] self.screen.load(res_id) else: if self.screen.current_view.view_type == 'form': self.sig_new(None, autosave=False) if self.screen.current_view.view_type \ in ('tree', 'graph', 'calendar'): self.screen.search_filter() self.update_revision()
def __init__(self, model, res_id=False, domain=None, mode=None, view_ids=None, context=None, name=False, limit=None, auto_refresh=False, search_value=None): super(Form, self).__init__() if not mode: mode = ['tree', 'form'] if domain is None: domain = [] if view_ids is None: view_ids = [] if context is None: context = {} self.model = model self.res_id = res_id self.domain = domain self.mode = mode self.context = context self.auto_refresh = auto_refresh self.view_ids = view_ids self.dialogs = [] self.screen = Screen(self.model, mode=mode, context=self.context, view_ids=view_ids, domain=domain, limit=limit, readonly=bool(auto_refresh), search_value=search_value) self.screen.widget.show() if not name: self.name = self.screen.current_view.title else: self.name = name self.create_tabcontent() self.screen.signal_connect(self, 'record-message', self._record_message) self.screen.signal_connect(self, 'record-modified', lambda *a: gobject.idle_add(self._record_modified, *a)) self.screen.signal_connect(self, 'record-saved', self._record_saved) self.screen.signal_connect(self, 'attachment-count', self._attachment_count) if res_id not in (None, False): if isinstance(res_id, (int, long)): res_id = [res_id] self.screen.load(res_id) else: if self.screen.current_view.view_type == 'form': self.sig_new(None, autosave=False) if self.screen.current_view.view_type \ in ('tree', 'graph', 'calendar'): self.screen.search_filter() if auto_refresh and int(auto_refresh): gobject.timeout_add(int(auto_refresh) * 1000, self.sig_reload)
def open_remote(self, record, create=True, changed=False, text=None, callback=None): group = record.value[self.attrs['name']] field = record.group.fields[self.attrs['name']] relation = field.attrs['relation'] context = field.context_get(record) access = common.MODELACCESS[relation] if not access['read']: return screen = Screen(relation, mode=['tree', 'form'], exclude_field=field.attrs.get('relation_field')) screen.pre_validate = bool(int(self.attrs.get('pre_validate', 0))) screen.group = group def open_callback(result): if callback: callback() WinForm(screen, open_callback, view_type='tree', context=context)
def click_and_relate(self, action, value, path): data = {} context = {} act = action.copy() if not(value): message(_('You must select a record to use the relation!')) return False from tryton.gui.window.view_form.screen import Screen screen = Screen(self.screen.group.fields[ path[1].name].attrs['relation']) screen.load([value]) encoder = PYSONEncoder() act['domain'] = encoder.encode(screen.current_record.expr_eval( act.get('domain', []), check_load=False)) act['context'] = encoder.encode(screen.current_record.expr_eval( act.get('context', {}), check_load=False)) data['model'] = self.screen.model_name data['id'] = value data['ids'] = [value] return Action._exec_action(act, data, context)
def open_remote(self, record, create=True, changed=False, text=None, callback=None): group = record.value[self.field_name] field = record.group.fields[self.field_name] relation = field.attrs['relation'] context = field.context_get(record) access = common.MODELACCESS[relation] if not access['read']: return screen = Screen(relation, mode=['tree', 'form'], exclude_field=field.attrs.get('relation_field')) screen.pre_validate = bool(int(self.attrs.get('pre_validate', 0))) screen.group = group def open_callback(result): if callback: callback() WinForm(screen, open_callback, view_type='tree', context=context)
def get_screen(self): domain = self.field.domain_get(self.record) context = self.field.context_get(self.record) return Screen(self.get_model(), domain=domain, context=context, mode=['form'], view_ids=self.attrs.get('view_ids', '').split(','), views_preload=self.attrs.get('views', {}), readonly=self._readonly, exclude_field=self.attrs.get('relation_field'))
def __init__(self, model, res_id=None, name='', **attributes): super(Form, self).__init__() self.model = model self.res_id = res_id self.mode = attributes.get('mode') self.view_ids = attributes.get('view_ids') self.dialogs = [] self.screen = Screen(self.model, **attributes) self.screen.widget.show() self.name = name self.create_tabcontent() self.set_buttons_sensitive() self.screen.signal_connect(self, 'record-message', self._record_message) self.screen.signal_connect( self, 'record-modified', lambda *a: gobject.idle_add(self._record_modified, *a)) self.screen.signal_connect(self, 'record-saved', self._record_saved) self.screen.signal_connect(self, 'attachment-count', self._attachment_count) self.screen.signal_connect(self, 'unread-note', self._unread_note) if res_id not in (None, False): if isinstance(res_id, (int, long)): res_id = [res_id] self.screen.load(res_id) else: if self.screen.current_view.view_type == 'form': self.sig_new(None, autosave=False) if self.screen.current_view.view_type \ in ('tree', 'graph', 'calendar'): self.screen.search_filter() self.update_revision()
def _get_screen_form(self): domain = self.field.domain_get(self.record) add_remove = self.record.expr_eval(self.attrs.get('add_remove')) if add_remove: domain = [domain, add_remove] context = self.field.get_context(self.record) # Remove the first tree view as mode is form only view_ids = self.attrs.get('view_ids', '').split(',')[1:] return Screen(self.attrs['relation'], domain=domain, view_ids=view_ids, mode=['form'], views_preload=self.attrs.get('views', {}), context=context)
def open_remote(self, record, create=True, changed=False, text=None, callback=None): field = record.group.fields[self.attrs['name']] relation = field.attrs['relation'] access = common.MODELACCESS[relation] if (create and not (self.attrs.get('create', True) and access['create'])): return elif not access['read']: return domain = field.domain_get(record) context = field.get_context(record) if create: obj_id = None elif not changed: obj_id = field.get(record) else: self.search_remote(record, field, text, callback=callback).show() return screen = Screen(relation, domain=domain, context=context, mode=['form'], view_ids=self.attrs.get('view_ids', '').split(','), exclude_field=field.attrs.get('relation_field')) def open_callback(result): if result: value = (screen.current_record.id, screen.current_record.rec_name()) field.set_client(record, value, force_change=True) if callback: callback() if obj_id: screen.load([obj_id]) WinForm(screen, open_callback, save_current=True, title=field.attrs.get('string')) else: WinForm(screen, open_callback, new=True, save_current=True, title=field.attrs.get('string'), rec_name=text)
def __init__(self, model, res_id=None, name='', **attributes): super(Form, self).__init__(**attributes) self.model = model self.res_id = res_id self.mode = attributes.get('mode') self.view_ids = attributes.get('view_ids') self.dialogs = [] if not name: name = common.MODELNAME.get(model) self.name = name loading_ids = res_id not in (None, False) if loading_ids: attributes.pop('tab_domain', None) self.screen = Screen(self.model, breadcrumb=[self.name], **attributes) self.screen.widget.show() self.screen.windows.append(self) self.create_tabcontent() self.set_buttons_sensitive() self.attachment_screen = None if loading_ids: if isinstance(res_id, int): res_id = [res_id] self.screen.load(res_id) else: if self.screen.current_view.view_type == 'form': self.sig_new(None, autosave=False) if self.screen.current_view.view_type \ in ('tree', 'graph', 'calendar'): self.screen.search_filter() self.update_revision() self.activate_save()
def __init__(self, user): self.parent = common.get_toplevel_window() self.win = gtk.Dialog(_("Preferences"), self.parent, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT) self.win.set_position(gtk.WIN_POS_CENTER_ON_PARENT) self.win.set_has_separator(False) self.win.set_icon(TRYTON_ICON) self.accel_group = gtk.AccelGroup() self.win.add_accel_group(self.accel_group) self.but_cancel = self.win.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL) self.but_ok = self.win.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK) self.but_ok.add_accelerator( "clicked", self.accel_group, gtk.keysyms.Return, gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE ) self.win.set_default_response(gtk.RESPONSE_OK) try: view = RPCExecute("model", "res.user", "get_preferences_fields_view") except RPCException: self.win.destroy() self.win = None return title = gtk.Label(_("Edit User Preferences")) title.show() self.win.vbox.pack_start(title, expand=False, fill=True) self.screen = Screen("res.user", mode=[]) self.screen.add_view(view) self.screen.new(default=False) try: preferences = RPCExecute("model", "res.user", "get_preferences", False) except RPCException: self.win.destroy() self.win = None return self.screen.current_record.set(preferences) self.screen.current_record.validate(softvalidation=True) self.screen.screen_container.set(self.screen.current_view.widget) self.screen.display(set_cursor=True) self.screen.widget.show() self.win.vbox.pack_start(self.screen.widget) self.win.set_title(_("Preference")) width, height = self.parent.get_size() self.win.set_default_size(int(width * 0.9), int(height * 0.9)) self.win.show()
def open_remote(self, record, create=True, changed=False, text=None, callback=None): field = record.group.fields[self.attrs['name']] relation = field.attrs['relation'] access = common.MODELACCESS[relation] if (create and not (self.attrs.get('create', True) and access['create'])): return elif not access['read']: return domain = field.domain_get(record) context = field.context_get(record) if create: obj_id = None elif not changed: obj_id = field.get(record) else: self.search_remote(record, relation, text, domain=domain, context=context, callback=callback).show() return screen = Screen(relation, domain=domain, context=context, mode=['form'], view_ids=self.attrs.get('view_ids', '').split(','), exclude_field=field.attrs.get('relation_field')) def open_callback(result): if result: value = (screen.current_record.id, screen.current_record.rec_name()) field.set_client(record, value, force_change=True) if callback: callback() if obj_id: screen.load([obj_id]) WinForm(screen, open_callback, save_current=True) else: WinForm(screen, open_callback, new=True, save_current=True)
def _attachment_preview_widget(self): vbox = Gtk.VBox(homogeneous=False, spacing=2) vbox.set_margin_start(4) hbox = Gtk.HBox(homogeneous=False, spacing=0) hbox.set_halign(Gtk.Align.CENTER) vbox.pack_start(hbox, expand=False, fill=True, padding=0) hbox.set_border_width(2) tooltips = common.Tooltips() but_prev = Gtk.Button() tooltips.set_tip(but_prev, _("Previous")) but_prev.add( common.IconFactory.get_image('tryton-back', Gtk.IconSize.SMALL_TOOLBAR)) but_prev.set_relief(Gtk.ReliefStyle.NONE) hbox.pack_start(but_prev, expand=False, fill=False, padding=0) label = Gtk.Label(label='(0,0)') hbox.pack_start(label, expand=False, fill=False, padding=0) but_next = Gtk.Button() tooltips.set_tip(but_next, _("Next")) but_next.add( common.IconFactory.get_image('tryton-forward', Gtk.IconSize.SMALL_TOOLBAR)) but_next.set_relief(Gtk.ReliefStyle.NONE) hbox.pack_start(but_next, expand=False, fill=False, padding=0) vbox.show_all() self.attachment_screen = screen = Screen('ir.attachment', readonly=True, mode=['form'], context={ 'preview': True, }) screen.widget.show() but_prev.connect('clicked', lambda *a: screen.display_prev()) but_next.connect('clicked', lambda *a: screen.display_next()) class Preview(): def record_message(self, position, length, *args): label.set_text('(%s/%s)' % (position or '_', length)) but_prev.set_sensitive(position and position > 1) but_next.set_sensitive(position and position < length) screen.windows.append(Preview()) vbox.pack_start(screen.widget, expand=True, fill=True, padding=0) return vbox
def _get_screen_form(self): domain = self.field.domain_get(self.record) add_remove = self.record.expr_eval(self.attrs.get('add_remove')) if add_remove: domain = [domain, add_remove] context = self.field.get_context(self.record) # Remove the first tree view as mode is form only view_ids = self.attrs.get('view_ids', '').split(',')[1:] model = self.attrs['relation'] breadcrumb = list(self.view.screen.breadcrumb) breadcrumb.append( self.attrs.get('string') or common.MODELNAME.get(model)) return Screen(model, domain=domain, view_ids=view_ids, mode=['form'], views_preload=self.attrs.get('views', {}), context=context, breadcrumb=breadcrumb)
def get_screen(self, search=False): domain = self.field.domain_get(self.record) if search: context = self.field.get_search_context(self.record) else: context = self.field.get_context(self.record) # Remove first tree view as mode is form only view_ids = self.attrs.get('view_ids', '').split(',')[1:] model = self.get_model() breadcrumb = list(self.view.screen.breadcrumb) breadcrumb.append( self.attrs.get('string') or common.MODELNAME.get(model)) return Screen(model, domain=domain, context=context, mode=['form'], view_ids=view_ids, views_preload=self.attrs.get('views', {}), readonly=self._readonly, exclude_field=self.attrs.get('relation_field'), breadcrumb=breadcrumb)
def _completion_action_activated(self, completion, index): if index == 0: self._sig_add(win_search=True) self.wid_text.grab_focus() elif index == 1: model = self.attrs['relation'] domain = self.field.domain_get(self.record) context = self.field.context_get(self.record) screen = Screen(model, domain, context=context, mode=['form']) def callback(result): self.focus_out = True if result: record = screen.current_record self.screen.load([record.id], modified=True) self.wid_text.set_text('') self.wid_text.grab_focus() self.focus_out = False WinForm(screen, callback, new=True, save_current=True)
def response(self, win, response_id): res = None if response_id == gtk.RESPONSE_OK: res = [r.id for r in self.screen.selected_records] elif response_id == gtk.RESPONSE_APPLY: self.screen.search_filter(self.screen.screen_container.get_text()) return elif response_id == gtk.RESPONSE_ACCEPT: # Remove first tree view as mode if form only view_ids = self.view_ids[1:] screen = Screen(self.model_name, domain=self.domain, context=self.context, order=self.order, mode=['form'], view_ids=view_ids, views_preload=self.views_preload) def callback(result): # JMO : TODO : see if that diff from tryton is needed if result and screen.save_current(): record = screen.current_record res = [(record.id, record.value.get('rec_name', ''))] self.callback(res) else: self.callback(None) self.destroy() WinForm(screen, callback, new=True, save_current=True, title=self.title) return if res: group = self.screen.group res = [(id_, group.get(id_).value.get('rec_name', '')) for id_ in res] self.callback(res) self.destroy()
def response(self, win, response_id): res = None if response_id == Gtk.ResponseType.OK: res = [r.id for r in self.screen.selected_records] elif response_id == Gtk.ResponseType.APPLY: self.screen.search_filter(self.screen.screen_container.get_text()) return elif response_id == Gtk.ResponseType.ACCEPT: # Remove first tree view as mode if form only view_ids = self.view_ids[1:] screen = Screen(self.model_name, domain=self.domain, context=self.context, order=self.order, mode=['form'], view_ids=view_ids, views_preload=self.views_preload, exclude_field=self.exclude_field, breadcrumb=[self.title]) def callback(result): if result: record = screen.current_record res = [(record.id, record.value.get('rec_name', ''))] self.callback(res) else: self.callback(None) self.destroy() WinForm(screen, callback, new=True, save_current=True) return if res: group = self.screen.group res = [(id_, group.get(id_).value.get('rec_name', '')) for id_ in res] self.callback(res) self.destroy()
def _sig_new(self): domain = self.field.domain_get(self.record) add_remove = self.record.expr_eval(self.attrs.get('add_remove')) if add_remove: domain = [domain, add_remove] context = self.field.context_get(self.record) screen = Screen(self.attrs['relation'], domain=domain, view_ids=self.attrs.get('view_ids', '').split(','), mode=['form'], views_preload=self.attrs.get('views', {}), context=context) def callback(result): self.focus_out = True if result: record = screen.current_record self.screen.load([record.id], modified=True) self.wid_text.set_text('') self.wid_text.grab_focus() self.focus_out = False WinForm(screen, callback, new=True, save_current=True)
def __init__(self, model, res_id=False, domain=None, order=None, mode=None, view_ids=None, context=None, name=False, limit=None, search_value=None, tab_domain=None, context_model=None): super(Form, self).__init__() if not mode: mode = ['tree', 'form'] if domain is None: domain = [] if view_ids is None: view_ids = [] self.model = model self.res_id = res_id self.domain = domain self.mode = mode self.context = context self.view_ids = view_ids self.dialogs = [] self.screen = Screen(self.model, mode=mode, context=context, view_ids=view_ids, domain=domain, limit=limit, order=order, search_value=search_value, tab_domain=tab_domain, context_model=context_model) self.screen.widget.show() if not name: self.name = self.screen.current_view.title else: self.name = name if self.model not in common.MODELHISTORY: self.menu_def = self.menu_def[:] # Remove callback to revision self.menu_def[11] = (self.menu_def[11][:2] + (None,) + self.menu_def[11][3:]) self.create_tabcontent() self.url_entry = url_entry = gtk.Entry() url_entry.show() url_entry.set_editable(False) style = url_entry.get_style() url_entry.modify_bg(gtk.STATE_ACTIVE, style.bg[gtk.STATE_INSENSITIVE]) self.widget.pack_start(url_entry, False, False) self.set_buttons_sensitive() self.screen.signal_connect(self, 'record-message', self._record_message) self.screen.signal_connect(self, 'record-modified', lambda *a: gobject.idle_add(self._record_modified, *a)) self.screen.signal_connect(self, 'record-saved', self._record_saved) self.screen.signal_connect(self, 'attachment-count', self._attachment_count) self.screen.signal_connect(self, 'unread-note', self._unread_note) if res_id not in (None, False): if isinstance(res_id, (int, long)): res_id = [res_id] self.screen.load(res_id) else: if self.screen.current_view.view_type == 'form': self.sig_new(None, autosave=False) if self.screen.current_view.view_type \ in ('tree', 'graph', 'calendar'): self.screen.search_filter() self.update_revision()
def __init__(self, view, attrs): super(One2Many, self).__init__(view, attrs) self.widget = Gtk.Frame() self.widget.set_shadow_type(Gtk.ShadowType.NONE) self.widget.get_accessible().set_name(attrs.get('string', '')) vbox = Gtk.VBox(homogeneous=False, spacing=2) self.widget.add(vbox) self._readonly = True self._required = False self._position = 0 self._length = 0 self.title_box = hbox = Gtk.HBox(homogeneous=False, spacing=0) hbox.set_border_width(2) self.title = Gtk.Label(label=set_underline(attrs.get('string', '')), use_underline=True, halign=Gtk.Align.START) hbox.pack_start(self.title, expand=True, fill=True, padding=0) hbox.pack_start(Gtk.VSeparator(), expand=False, fill=True, padding=0) tooltips = common.Tooltips() but_switch = Gtk.Button(can_focus=False) tooltips.set_tip(but_switch, _('Switch')) but_switch.connect('clicked', self.switch_view) but_switch.add( common.IconFactory.get_image('tryton-switch', Gtk.IconSize.SMALL_TOOLBAR)) but_switch.set_relief(Gtk.ReliefStyle.NONE) hbox.pack_start(but_switch, expand=False, fill=False, padding=0) self.but_pre = Gtk.Button(can_focus=False) tooltips.set_tip(self.but_pre, _('Previous')) self.but_pre.connect('clicked', self._sig_previous) self.but_pre.add( common.IconFactory.get_image('tryton-back', Gtk.IconSize.SMALL_TOOLBAR)) self.but_pre.set_relief(Gtk.ReliefStyle.NONE) hbox.pack_start(self.but_pre, expand=False, fill=False, padding=0) self.label = Gtk.Label(label='(0,0)') hbox.pack_start(self.label, expand=False, fill=False, padding=0) self.but_next = Gtk.Button(can_focus=False) tooltips.set_tip(self.but_next, _('Next')) self.but_next.connect('clicked', self._sig_next) self.but_next.add( common.IconFactory.get_image('tryton-forward', Gtk.IconSize.SMALL_TOOLBAR)) self.but_next.set_relief(Gtk.ReliefStyle.NONE) hbox.pack_start(self.but_next, expand=False, fill=False, padding=0) hbox.pack_start(Gtk.VSeparator(), expand=False, fill=True, padding=0) self.focus_out = True self.wid_completion = None if attrs.get('add_remove'): self.wid_text = Gtk.Entry() self.wid_text.set_placeholder_text(_('Search')) self.wid_text.set_property('width_chars', 13) self.wid_text.connect('focus-out-event', self._focus_out) hbox.pack_start(self.wid_text, expand=True, fill=True, padding=0) if int(self.attrs.get('completion', 1)): self.wid_completion = get_completion(search=self.read_access, create=self.create_access) self.wid_completion.connect('match-selected', self._completion_match_selected) self.wid_completion.connect('action-activated', self._completion_action_activated) self.wid_text.set_completion(self.wid_completion) self.wid_text.connect('changed', self._update_completion) self.but_add = Gtk.Button(can_focus=False) tooltips.set_tip(self.but_add, _('Add existing record')) self.but_add.connect('clicked', self._sig_add) self.but_add.add( common.IconFactory.get_image('tryton-add', Gtk.IconSize.SMALL_TOOLBAR)) self.but_add.set_relief(Gtk.ReliefStyle.NONE) hbox.pack_start(self.but_add, expand=False, fill=False, padding=0) self.but_remove = Gtk.Button(can_focus=False) tooltips.set_tip(self.but_remove, _('Remove selected record')) self.but_remove.connect('clicked', self._sig_remove, True) self.but_remove.add( common.IconFactory.get_image('tryton-remove', Gtk.IconSize.SMALL_TOOLBAR)) self.but_remove.set_relief(Gtk.ReliefStyle.NONE) hbox.pack_start(self.but_remove, expand=False, fill=False, padding=0) hbox.pack_start(Gtk.VSeparator(), expand=False, fill=True, padding=0) self.but_new = Gtk.Button(can_focus=False) tooltips.set_tip(self.but_new, _('Create a new record')) self.but_new.connect('clicked', self._sig_new) self.but_new.add( common.IconFactory.get_image('tryton-create', Gtk.IconSize.SMALL_TOOLBAR)) self.but_new.set_relief(Gtk.ReliefStyle.NONE) hbox.pack_start(self.but_new, expand=False, fill=False, padding=0) self.but_open = Gtk.Button(can_focus=False) tooltips.set_tip(self.but_open, _('Edit selected record')) self.but_open.connect('clicked', self._sig_edit) self.but_open.add( common.IconFactory.get_image('tryton-open', Gtk.IconSize.SMALL_TOOLBAR)) self.but_open.set_relief(Gtk.ReliefStyle.NONE) hbox.pack_start(self.but_open, expand=False, fill=False, padding=0) self.but_del = Gtk.Button(can_focus=False) tooltips.set_tip(self.but_del, _('Delete selected record')) self.but_del.connect('clicked', self._sig_remove, False) self.but_del.add( common.IconFactory.get_image('tryton-delete', Gtk.IconSize.SMALL_TOOLBAR)) self.but_del.set_relief(Gtk.ReliefStyle.NONE) hbox.pack_start(self.but_del, expand=False, fill=False, padding=0) self.but_undel = Gtk.Button(can_focus=False) tooltips.set_tip(self.but_undel, _('Undelete selected record <Ins>')) self.but_undel.connect('clicked', self._sig_undelete) self.but_undel.add( common.IconFactory.get_image('tryton-undo', Gtk.IconSize.SMALL_TOOLBAR)) self.but_undel.set_relief(Gtk.ReliefStyle.NONE) hbox.pack_start(self.but_undel, expand=False, fill=False, padding=0) tooltips.enable() frame = Gtk.Frame() frame.add(hbox) frame.set_shadow_type(Gtk.ShadowType.OUT) vbox.pack_start(frame, expand=False, fill=True, padding=0) model = attrs['relation'] breadcrumb = list(self.view.screen.breadcrumb) breadcrumb.append(attrs.get('string') or common.MODELNAME.get(model)) self.screen = Screen(model, mode=attrs.get('mode', 'tree,form').split(','), view_ids=attrs.get('view_ids', '').split(','), views_preload=attrs.get('views', {}), order=attrs.get('order'), row_activate=self._on_activate, exclude_field=attrs.get('relation_field', None), limit=None, breadcrumb=breadcrumb) self.screen.pre_validate = bool(int(attrs.get('pre_validate', 0))) self.screen.windows.append(self) vbox.pack_start(self.screen.widget, expand=True, fill=True, padding=0) self.title.set_mnemonic_widget( self.screen.current_view.mnemonic_widget) self.screen.widget.connect('key_press_event', self.on_keypress) if self.attrs.get('add_remove'): self.wid_text.connect('key_press_event', self.on_keypress) but_switch.props.sensitive = self.screen.number_of_views > 1
def __init__(self, view, attrs): super(Many2Many, self).__init__(view, attrs) self.widget = gtk.Frame() self.widget.set_shadow_type(gtk.SHADOW_NONE) self.widget.get_accessible().set_name(attrs.get('string', '')) vbox = gtk.VBox(homogeneous=False, spacing=5) self.widget.add(vbox) self._readonly = True self._required = False self._position = 0 hbox = gtk.HBox(homogeneous=False, spacing=0) hbox.set_border_width(2) self.title = gtk.Label(attrs.get('string', '')) self.title.set_alignment(0.0, 0.5) hbox.pack_start(self.title, expand=True, fill=True) hbox.pack_start(gtk.VSeparator(), expand=False, fill=True) tooltips = common.Tooltips() self.wid_text = PlaceholderEntry() self.wid_text.set_placeholder_text(_('Search')) self.wid_text.set_property('width_chars', 13) self.wid_text.connect('focus-out-event', lambda *a: self._focus_out()) self.focus_out = True hbox.pack_start(self.wid_text, expand=True, fill=True) if int(self.attrs.get('completion', 1)): self.wid_completion = get_completion( create=self.attrs.get('create', True) and common.MODELACCESS[self.attrs['relation']]['create']) self.wid_completion.connect('match-selected', self._completion_match_selected) self.wid_completion.connect('action-activated', self._completion_action_activated) self.wid_text.set_completion(self.wid_completion) self.wid_text.connect('changed', self._update_completion) else: self.wid_completion = None self.but_add = gtk.Button() tooltips.set_tip(self.but_add, _('Add existing record')) self.but_add.connect('clicked', self._sig_add) img_add = gtk.Image() img_add.set_from_stock('tryton-list-add', gtk.ICON_SIZE_SMALL_TOOLBAR) img_add.set_alignment(0.5, 0.5) self.but_add.add(img_add) self.but_add.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_add, expand=False, fill=False) self.but_remove = gtk.Button() tooltips.set_tip(self.but_remove, _('Remove selected record <Del>')) self.but_remove.connect('clicked', self._sig_remove) img_remove = gtk.Image() img_remove.set_from_stock('tryton-list-remove', gtk.ICON_SIZE_SMALL_TOOLBAR) img_remove.set_alignment(0.5, 0.5) self.but_remove.add(img_remove) self.but_remove.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_remove, expand=False, fill=False) hbox.set_focus_chain([self.wid_text]) tooltips.enable() frame = gtk.Frame() frame.add(hbox) frame.set_shadow_type(gtk.SHADOW_OUT) vbox.pack_start(frame, expand=False, fill=True) self.screen = Screen(attrs['relation'], view_ids=attrs.get('view_ids', '').split(','), mode=['tree'], views_preload=attrs.get('views', {}), row_activate=self._on_activate, limit=None) self.screen.signal_connect(self, 'record-message', self._sig_label) vbox.pack_start(self.screen.widget, expand=True, fill=True) self.screen.widget.connect('key_press_event', self.on_keypress) self.wid_text.connect('key_press_event', self.on_keypress)
def __init__(self, model, callback, sel_multi=True, context=None, domain=None, order=None, view_ids=None, views_preload=None, new=True, title='', exclude_field=None): NoModal.__init__(self) if view_ids is None: view_ids = [] if views_preload is None: views_preload = {} self.domain = domain or [] self.context = context or {} self.order = order self.view_ids = view_ids self.views_preload = views_preload self.sel_multi = sel_multi self.callback = callback self.title = title self.exclude_field = exclude_field self.win = Gtk.Dialog( title=_('Search'), transient_for=self.parent, destroy_with_parent=True) Main().add_window(self.win) self.win.set_icon(TRYTON_ICON) self.win.set_position(Gtk.WindowPosition.CENTER_ON_PARENT) self.win.set_default_response(Gtk.ResponseType.APPLY) self.win.connect('response', self.response) self.win.set_default_size(*self.default_size()) self.accel_group = Gtk.AccelGroup() self.win.add_accel_group(self.accel_group) self.but_cancel = self.win.add_button( set_underline(_("Cancel")), Gtk.ResponseType.CANCEL) self.but_cancel.set_image(common.IconFactory.get_image( 'tryton-cancel', Gtk.IconSize.BUTTON)) self.but_cancel.set_always_show_image(True) self.but_find = self.win.add_button( set_underline(_("Search")), Gtk.ResponseType.APPLY) self.but_find.set_image(common.IconFactory.get_image( 'tryton-search', Gtk.IconSize.BUTTON)) self.but_find.set_always_show_image(True) if new and common.MODELACCESS[model]['create']: self.but_new = self.win.add_button( set_underline(_("New")), Gtk.ResponseType.ACCEPT) self.but_new.set_image(common.IconFactory.get_image( 'tryton-create', Gtk.IconSize.BUTTON)) self.but_new.set_always_show_image(True) self.but_new.set_accel_path('<tryton>/Form/New', self.accel_group) self.but_ok = self.win.add_button( set_underline(_("OK")), Gtk.ResponseType.OK) self.but_ok.set_image(common.IconFactory.get_image( 'tryton-ok', Gtk.IconSize.BUTTON)) self.but_ok.set_always_show_image(True) self.but_ok.add_accelerator( 'clicked', self.accel_group, Gdk.KEY_Return, Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE) hbox = Gtk.HBox() hbox.show() self.win.vbox.pack_start(hbox, expand=False, fill=True, padding=0) self.win.vbox.pack_start( Gtk.HSeparator(), expand=False, fill=True, padding=0) self.screen = Screen(model, domain=domain, mode=['tree'], order=order, context=context, view_ids=view_ids, views_preload=views_preload, row_activate=self.sig_activate, readonly=True) self.view = self.screen.current_view # Prevent to set tree_state self.screen.tree_states_done.add(id(self.view)) sel = self.view.treeview.get_selection() self.win.set_title(_('Search %s') % self.title) if not sel_multi: sel.set_mode(Gtk.SelectionMode.SINGLE) else: sel.set_mode(Gtk.SelectionMode.MULTIPLE) self.win.vbox.pack_start( self.screen.widget, expand=True, fill=True, padding=0) self.screen.widget.show() self.model_name = model self.register()
def __init__(self, view, attrs): super(One2Many, self).__init__(view, attrs) self.widget = gtk.Frame() self.widget.set_shadow_type(gtk.SHADOW_NONE) self.widget.get_accessible().set_name(attrs.get('string', '')) vbox = gtk.VBox(homogeneous=False, spacing=2) self.widget.add(vbox) self._readonly = True self._position = 0 self._length = 0 self.title_box = hbox = gtk.HBox(homogeneous=False, spacing=0) hbox.set_border_width(2) label = gtk.Label(attrs.get('string', '')) label.set_alignment(0.0, 0.5) hbox.pack_start(label, expand=True, fill=True) hbox.pack_start(gtk.VSeparator(), expand=False, fill=True) tooltips = common.Tooltips() self.focus_out = True self.wid_completion = None if attrs.get('add_remove'): self.wid_text = PlaceholderEntry() self.wid_text.set_placeholder_text(_('Search')) self.wid_text.set_property('width_chars', 13) self.wid_text.connect('focus-out-event', lambda *a: self._focus_out()) hbox.pack_start(self.wid_text, expand=True, fill=True) if int(self.attrs.get('completion', 1)): access = common.MODELACCESS[attrs['relation']] self.wid_completion = get_completion( search=access['read'] and access['write'], create=attrs.get('create', True) and access['create']) self.wid_completion.connect('match-selected', self._completion_match_selected) self.wid_completion.connect('action-activated', self._completion_action_activated) self.wid_text.set_completion(self.wid_completion) self.wid_text.connect('changed', self._update_completion) self.but_add = gtk.Button() tooltips.set_tip(self.but_add, _('Add existing record')) self.but_add.connect('clicked', self._sig_add) img_add = gtk.Image() img_add.set_from_stock('tryton-list-add', gtk.ICON_SIZE_SMALL_TOOLBAR) img_add.set_alignment(0.5, 0.5) self.but_add.add(img_add) self.but_add.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_add, expand=False, fill=False) self.but_remove = gtk.Button() tooltips.set_tip(self.but_remove, _('Remove selected record')) self.but_remove.connect('clicked', self._sig_remove, True) img_remove = gtk.Image() img_remove.set_from_stock('tryton-list-remove', gtk.ICON_SIZE_SMALL_TOOLBAR) img_remove.set_alignment(0.5, 0.5) self.but_remove.add(img_remove) self.but_remove.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_remove, expand=False, fill=False) hbox.pack_start(gtk.VSeparator(), expand=False, fill=True) self.but_new = gtk.Button() tooltips.set_tip(self.but_new, _('Create a new record <F3>')) self.but_new.connect('clicked', self._sig_new) img_new = gtk.Image() img_new.set_from_stock('tryton-new', gtk.ICON_SIZE_SMALL_TOOLBAR) img_new.set_alignment(0.5, 0.5) self.but_new.add(img_new) self.but_new.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_new, expand=False, fill=False) self.but_open = gtk.Button() tooltips.set_tip(self.but_open, _('Edit selected record <F2>')) self.but_open.connect('clicked', self._sig_edit) img_open = gtk.Image() img_open.set_from_stock('tryton-open', gtk.ICON_SIZE_SMALL_TOOLBAR) img_open.set_alignment(0.5, 0.5) self.but_open.add(img_open) self.but_open.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_open, expand=False, fill=False) self.but_del = gtk.Button() tooltips.set_tip(self.but_del, _('Delete selected record <Del>')) self.but_del.connect('clicked', self._sig_remove, False) img_del = gtk.Image() img_del.set_from_stock('tryton-delete', gtk.ICON_SIZE_SMALL_TOOLBAR) img_del.set_alignment(0.5, 0.5) self.but_del.add(img_del) self.but_del.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_del, expand=False, fill=False) self.but_undel = gtk.Button() tooltips.set_tip(self.but_undel, _('Undelete selected record <Ins>')) self.but_undel.connect('clicked', self._sig_undelete) img_undel = gtk.Image() img_undel.set_from_stock('tryton-undo', gtk.ICON_SIZE_SMALL_TOOLBAR) img_undel.set_alignment(0.5, 0.5) self.but_undel.add(img_undel) self.but_undel.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_undel, expand=False, fill=False) hbox.pack_start(gtk.VSeparator(), expand=False, fill=True) self.but_pre = gtk.Button() tooltips.set_tip(self.but_pre, _('Previous')) self.but_pre.connect('clicked', self._sig_previous) img_pre = gtk.Image() img_pre.set_from_stock('tryton-go-previous', gtk.ICON_SIZE_SMALL_TOOLBAR) img_pre.set_alignment(0.5, 0.5) self.but_pre.add(img_pre) self.but_pre.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_pre, expand=False, fill=False) self.label = gtk.Label('(0,0)') hbox.pack_start(self.label, expand=False, fill=False) self.but_next = gtk.Button() tooltips.set_tip(self.but_next, _('Next')) self.but_next.connect('clicked', self._sig_next) img_next = gtk.Image() img_next.set_from_stock('tryton-go-next', gtk.ICON_SIZE_SMALL_TOOLBAR) img_next.set_alignment(0.5, 0.5) self.but_next.add(img_next) self.but_next.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_next, expand=False, fill=False) hbox.pack_start(gtk.VSeparator(), expand=False, fill=True) but_switch = gtk.Button() tooltips.set_tip(but_switch, _('Switch')) but_switch.connect('clicked', self.switch_view) img_switch = gtk.Image() img_switch.set_from_stock('tryton-fullscreen', gtk.ICON_SIZE_SMALL_TOOLBAR) img_switch.set_alignment(0.5, 0.5) but_switch.add(img_switch) but_switch.set_relief(gtk.RELIEF_NONE) hbox.pack_start(but_switch, expand=False, fill=False) if attrs.get('add_remove'): hbox.set_focus_chain([self.wid_text]) else: hbox.set_focus_chain([]) tooltips.enable() frame = gtk.Frame() frame.add(hbox) frame.set_shadow_type(gtk.SHADOW_OUT) vbox.pack_start(frame, expand=False, fill=True) self.screen = Screen(attrs['relation'], mode=attrs.get('mode', 'tree,form').split(','), view_ids=attrs.get('view_ids', '').split(','), views_preload=attrs.get('views', {}), row_activate=self._on_activate, readonly=self.attrs.get('readonly', False), exclude_field=attrs.get('relation_field', None)) self.screen.pre_validate = bool(int(attrs.get('pre_validate', 0))) self.screen.signal_connect(self, 'record-message', self._sig_label) vbox.pack_start(self.screen.widget, expand=True, fill=True) self.screen.widget.connect('key_press_event', self.on_keypress) if self.attrs.get('add_remove'): self.wid_text.connect('key_press_event', self.on_keypress) but_switch.props.sensitive = self.screen.number_of_views > 1
def __init__(self, model, callback, sel_multi=True, context=None, domain=None, view_ids=None, views_preload=None, new=True, title=''): NoModal.__init__(self) if views_preload is None: views_preload = {} self.domain = domain or [] self.context = context or {} self.sel_multi = sel_multi self.callback = callback self.title = title self.win = gtk.Dialog(_('Search'), self.parent, gtk.DIALOG_DESTROY_WITH_PARENT) self.win.set_icon(GNUHEALTH_ICON) self.win.set_default_response(gtk.RESPONSE_APPLY) self.win.connect('response', self.response) self.accel_group = gtk.AccelGroup() self.win.add_accel_group(self.accel_group) self.but_cancel = self.win.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL) self.but_find = self.win.add_button(gtk.STOCK_FIND, gtk.RESPONSE_APPLY) if new and common.MODELACCESS[model]['create']: self.but_new = self.win.add_button(gtk.STOCK_NEW, gtk.RESPONSE_ACCEPT) self.but_new.set_accel_path('<tryton>/Form/New', self.accel_group) self.but_ok = self.win.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK) self.but_ok.add_accelerator('clicked', self.accel_group, gtk.keysyms.Return, gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE) hbox = gtk.HBox() hbox.show() self.win.vbox.pack_start(hbox, expand=False, fill=True) self.win.vbox.pack_start(gtk.HSeparator(), expand=False, fill=True) scrollwindow = gtk.ScrolledWindow() scrollwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) self.win.vbox.pack_start(scrollwindow, expand=True, fill=True) self.screen = Screen(model, domain=domain, mode=['tree'], context=context, view_ids=view_ids, views_preload=views_preload, row_activate=self.sig_activate) self.view = self.screen.current_view self.view.unset_editable() # Prevent to set tree_state self.screen.tree_states_done.add(id(self.view)) sel = self.view.treeview.get_selection() self.win.set_title(_('Search %s') % self.title) if not sel_multi: sel.set_mode(gtk.SELECTION_SINGLE) else: sel.set_mode(gtk.SELECTION_MULTIPLE) viewport = gtk.Viewport() viewport.set_shadow_type(gtk.SHADOW_NONE) viewport.add(self.screen.widget) self.screen.widget.show() viewport.show() scrollwindow.add(viewport) scrollwindow.show() self.model_name = model self.win.set_default_size(700, 500) self.register() sensible_allocation = self.sensible_widget.get_allocation() self.win.set_default_size(int(sensible_allocation.width * 0.9), int(sensible_allocation.height * 0.9))
def __init__(self, model, callback, sel_multi=True, context=None, domain=None, view_ids=None, views_preload=None, new=True): NoModal.__init__(self) if views_preload is None: views_preload = {} self.domain = domain or [] self.context = context or {} self.sel_multi = sel_multi self.callback = callback self.win = gtk.Dialog(_('Search'), self.parent, gtk.DIALOG_DESTROY_WITH_PARENT) self.win.set_icon(TRYTON_ICON) self.win.set_has_separator(True) self.win.set_default_response(gtk.RESPONSE_APPLY) self.win.connect('response', self.response) self.accel_group = gtk.AccelGroup() self.win.add_accel_group(self.accel_group) self.but_cancel = self.win.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL) self.but_find = self.win.add_button(gtk.STOCK_FIND, gtk.RESPONSE_APPLY) if new and common.MODELACCESS[model]['create']: self.but_new = self.win.add_button(gtk.STOCK_NEW, gtk.RESPONSE_ACCEPT) self.but_new.set_accel_path('<tryton>/Form/New', self.accel_group) self.but_ok = self.win.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK) self.but_ok.add_accelerator('clicked', self.accel_group, gtk.keysyms.Return, gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE) hbox = gtk.HBox() hbox.show() self.win.vbox.pack_start(hbox, expand=False, fill=True) self.win.vbox.pack_start(gtk.HSeparator(), expand=False, fill=True) scrollwindow = gtk.ScrolledWindow() scrollwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) self.win.vbox.pack_start(scrollwindow, expand=True, fill=True) self.screen = Screen(model, domain=domain, mode=['tree'], context=context, view_ids=view_ids, views_preload=views_preload, row_activate=self.sig_activate) self.view = self.screen.current_view self.view.unset_editable() # Prevent to set tree_state self.screen.tree_states_done.add(id(self.view)) sel = self.view.treeview.get_selection() if not sel_multi: sel.set_mode(gtk.SELECTION_SINGLE) else: sel.set_mode(gtk.SELECTION_MULTIPLE) viewport = gtk.Viewport() viewport.set_shadow_type(gtk.SHADOW_NONE) viewport.add(self.screen.widget) self.screen.widget.show() viewport.show() scrollwindow.add(viewport) scrollwindow.show() self.model_name = model self.win.set_default_size(700, 500) self.register() sensible_allocation = self.sensible_widget.get_allocation() self.win.set_default_size(int(sensible_allocation.width * 0.95), int(sensible_allocation.height * 0.95))
class WinSearch(NoModal): def __init__(self, model, callback, sel_multi=True, context=None, domain=None, view_ids=None, views_preload=None, new=True): NoModal.__init__(self) if views_preload is None: views_preload = {} self.domain = domain or [] self.context = context or {} self.sel_multi = sel_multi self.callback = callback self.win = gtk.Dialog(_('Search'), self.parent, gtk.DIALOG_DESTROY_WITH_PARENT) self.win.set_icon(TRYTON_ICON) self.win.set_has_separator(True) self.win.set_default_response(gtk.RESPONSE_APPLY) self.win.connect('response', self.response) self.accel_group = gtk.AccelGroup() self.win.add_accel_group(self.accel_group) self.but_cancel = self.win.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL) self.but_find = self.win.add_button(gtk.STOCK_FIND, gtk.RESPONSE_APPLY) if new and common.MODELACCESS[model]['create']: self.but_new = self.win.add_button(gtk.STOCK_NEW, gtk.RESPONSE_ACCEPT) self.but_new.set_accel_path('<tryton>/Form/New', self.accel_group) self.but_ok = self.win.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK) self.but_ok.add_accelerator('clicked', self.accel_group, gtk.keysyms.Return, gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE) hbox = gtk.HBox() hbox.show() self.win.vbox.pack_start(hbox, expand=False, fill=True) self.win.vbox.pack_start(gtk.HSeparator(), expand=False, fill=True) scrollwindow = gtk.ScrolledWindow() scrollwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) self.win.vbox.pack_start(scrollwindow, expand=True, fill=True) self.screen = Screen(model, domain=domain, mode=['tree'], context=context, view_ids=view_ids, views_preload=views_preload, row_activate=self.sig_activate) self.view = self.screen.current_view self.view.unset_editable() # Prevent to set tree_state self.screen.tree_states_done.add(id(self.view)) sel = self.view.treeview.get_selection() if not sel_multi: sel.set_mode(gtk.SELECTION_SINGLE) else: sel.set_mode(gtk.SELECTION_MULTIPLE) viewport = gtk.Viewport() viewport.set_shadow_type(gtk.SHADOW_NONE) viewport.add(self.screen.widget) self.screen.widget.show() viewport.show() scrollwindow.add(viewport) scrollwindow.show() self.model_name = model self.win.set_default_size(700, 500) self.register() sensible_allocation = self.sensible_widget.get_allocation() self.win.set_default_size(int(sensible_allocation.width * 0.95), int(sensible_allocation.height * 0.95)) def sig_activate(self, *args): self.view.treeview.emit_stop_by_name('row_activated') self.win.response(gtk.RESPONSE_OK) return True def destroy(self): self.screen.destroy() self.win.destroy() NoModal.destroy(self) def show(self): self.win.show() common.center_window(self.win, self.parent, self.sensible_widget) def hide(self): self.win.hide() def response(self, win, response_id): res = None if response_id == gtk.RESPONSE_OK: res = [r.id for r in self.screen.selected_records] elif response_id == gtk.RESPONSE_APPLY: self.screen.search_filter(self.screen.screen_container.get_text()) return elif response_id == gtk.RESPONSE_ACCEPT: screen = Screen(self.model_name, domain=self.domain, context=self.context, mode=['form']) def callback(result): if result and screen.save_current(): record = screen.current_record res = [(record.id, record.value.get('rec_name', ''))] self.callback(res) else: self.callback(None) self.destroy() WinForm(screen, callback, new=True, save_current=True) return if res: group = self.screen.group res = [(id_, group.get(id_).value.get('rec_name', '')) for id_ in res] self.callback(res) self.destroy()
def __init__(self, field_name, model_name, attrs=None): super(One2Many, self).__init__(field_name, model_name, attrs=attrs) self.widget = gtk.VBox(homogeneous=False, spacing=2) self._readonly = True hbox = gtk.HBox(homogeneous=False, spacing=0) hbox.set_border_width(2) label = gtk.Label(attrs.get('string', '')) label.set_alignment(0.0, 0.5) hbox.pack_start(label, expand=True, fill=True) hbox.pack_start(gtk.VSeparator(), expand=False, fill=True) tooltips = common.Tooltips() if attrs.get('add_remove'): self.wid_text = gtk.Entry() self.wid_text.set_property('width_chars', 13) self.wid_text.connect('activate', self._sig_activate) self.wid_text.connect('focus-out-event', self._focus_out) hbox.pack_start(self.wid_text, expand=True, fill=True) self.but_add = gtk.Button() tooltips.set_tip(self.but_add, _('Add')) self.but_add.connect('clicked', self._sig_add) img_add = gtk.Image() img_add.set_from_stock('tryton-list-add', gtk.ICON_SIZE_SMALL_TOOLBAR) img_add.set_alignment(0.5, 0.5) self.but_add.add(img_add) self.but_add.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_add, expand=False, fill=False) self.but_remove = gtk.Button() tooltips.set_tip(self.but_remove, _('Remove <Del>')) self.but_remove.connect('clicked', self._sig_remove, True) img_remove = gtk.Image() img_remove.set_from_stock('tryton-list-remove', gtk.ICON_SIZE_SMALL_TOOLBAR) img_remove.set_alignment(0.5, 0.5) self.but_remove.add(img_remove) self.but_remove.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_remove, expand=False, fill=False) hbox.pack_start(gtk.VSeparator(), expand=False, fill=True) self.but_new = gtk.Button() tooltips.set_tip(self.but_new, _('Create a new record <F3>')) self.but_new.connect('clicked', self._sig_new) img_new = gtk.Image() img_new.set_from_stock('tryton-new', gtk.ICON_SIZE_SMALL_TOOLBAR) img_new.set_alignment(0.5, 0.5) self.but_new.add(img_new) self.but_new.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_new, expand=False, fill=False) self.but_open = gtk.Button() tooltips.set_tip(self.but_open, _('Edit selected record <F2>')) self.but_open.connect('clicked', self._sig_edit) img_open = gtk.Image() img_open.set_from_stock('tryton-open', gtk.ICON_SIZE_SMALL_TOOLBAR) img_open.set_alignment(0.5, 0.5) self.but_open.add(img_open) self.but_open.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_open, expand=False, fill=False) self.but_del = gtk.Button() tooltips.set_tip(self.but_del, _('Delete selected record <Del>')) self.but_del.connect('clicked', self._sig_remove, False) img_del = gtk.Image() img_del.set_from_stock('tryton-delete', gtk.ICON_SIZE_SMALL_TOOLBAR) img_del.set_alignment(0.5, 0.5) self.but_del.add(img_del) self.but_del.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_del, expand=False, fill=False) self.but_undel = gtk.Button() tooltips.set_tip(self.but_undel, _('Undelete selected record <Ins>')) self.but_undel.connect('clicked', self._sig_undelete) img_undel = gtk.Image() img_undel.set_from_stock('tryton-undo', gtk.ICON_SIZE_SMALL_TOOLBAR) img_undel.set_alignment(0.5, 0.5) self.but_undel.add(img_undel) self.but_undel.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_undel, expand=False, fill=False) hbox.pack_start(gtk.VSeparator(), expand=False, fill=True) self.but_pre = gtk.Button() tooltips.set_tip(self.but_pre, _('Previous')) self.but_pre.connect('clicked', self._sig_previous) img_pre = gtk.Image() img_pre.set_from_stock('tryton-go-previous', gtk.ICON_SIZE_SMALL_TOOLBAR) img_pre.set_alignment(0.5, 0.5) self.but_pre.add(img_pre) self.but_pre.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_pre, expand=False, fill=False) self.label = gtk.Label('(0,0)') hbox.pack_start(self.label, expand=False, fill=False) self.but_next = gtk.Button() tooltips.set_tip(self.but_next, _('Next')) self.but_next.connect('clicked', self._sig_next) img_next = gtk.Image() img_next.set_from_stock('tryton-go-next', gtk.ICON_SIZE_SMALL_TOOLBAR) img_next.set_alignment(0.5, 0.5) self.but_next.add(img_next) self.but_next.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_next, expand=False, fill=False) hbox.pack_start(gtk.VSeparator(), expand=False, fill=True) but_switch = gtk.Button() tooltips.set_tip(but_switch, _('Switch')) but_switch.connect('clicked', self.switch_view) img_switch = gtk.Image() img_switch.set_from_stock('tryton-fullscreen', gtk.ICON_SIZE_SMALL_TOOLBAR) img_switch.set_alignment(0.5, 0.5) but_switch.add(img_switch) but_switch.set_relief(gtk.RELIEF_NONE) hbox.pack_start(but_switch, expand=False, fill=False) if attrs.get('add_remove'): hbox.set_focus_chain([self.wid_text]) else: hbox.set_focus_chain([]) tooltips.enable() frame = gtk.Frame() frame.add(hbox) frame.set_shadow_type(gtk.SHADOW_OUT) self.widget.pack_start(frame, expand=False, fill=True) self.screen = Screen(attrs['relation'], mode=attrs.get('mode', 'tree,form').split(','), view_ids=attrs.get('view_ids', '').split(','), views_preload=attrs.get('views', {}), row_activate=self._on_activate, exclude_field=attrs.get('relation_field', None)) self.screen.signal_connect(self, 'record-message', self._sig_label) self.widget.pack_start(self.screen.widget, expand=True, fill=True) self.screen.widget.connect('key_press_event', self.on_keypress) if self.attrs.get('add_remove'): self.wid_text.connect('key_press_event', self.on_keypress)
class Preference(NoModal): "Preference window" def __init__(self, user, callback): NoModal.__init__(self) self.callback = callback self.win = gtk.Dialog(_('Preferences'), self.parent, gtk.DIALOG_DESTROY_WITH_PARENT) self.win.set_position(gtk.WIN_POS_CENTER_ON_PARENT) self.win.set_has_separator(False) self.win.set_icon(TRYTON_ICON) self.accel_group = gtk.AccelGroup() self.win.add_accel_group(self.accel_group) self.but_cancel = self.win.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL) self.but_ok = self.win.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK) self.but_ok.add_accelerator('clicked', self.accel_group, gtk.keysyms.Return, gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE) self.win.set_default_response(gtk.RESPONSE_OK) self.win.connect('response', self.response) try: view = RPCExecute('model', 'res.user', 'get_preferences_fields_view') except RPCException: self.win.destroy() self.win = None return title = gtk.Label(_('Edit User Preferences')) title.show() self.win.vbox.pack_start(title, expand=False, fill=True) self.screen = Screen('res.user', mode=[]) # Reset readonly set automaticly by MODELACCESS self.screen.readonly = False self.screen.group.readonly = False self.screen.group.skip_model_access = True self.screen.add_view(view) self.screen.switch_view() self.screen.new(default=False) try: preferences = RPCExecute('model', 'res.user', 'get_preferences', False) except RPCException: self.win.destroy() self.win = None return self.screen.current_record.set(preferences) self.screen.current_record.id = rpc._USER self.screen.current_record.validate(softvalidation=True) self.screen.display(set_cursor=True) self.screen.widget.show() self.win.vbox.pack_start(self.screen.widget) self.win.set_title(_('Preference')) width, height = self.parent.get_size() self.win.set_default_size(int(width * 0.9), int(height * 0.9)) self.register() self.win.show() def response(self, win, response_id): if response_id == gtk.RESPONSE_OK: if self.screen.current_record.validate(): vals = copy.copy(self.screen.get()) if 'password' in vals: password = common.ask(_('Current Password:'******'model', 'res.user', 'set_preferences', vals, password) except RPCException: return self.parent.present() self.destroy() self.callback() def destroy(self): self.screen.destroy() self.win.destroy() NoModal.destroy(self)
class Form(SignalEvent, TabContent): "Form" toolbar_def = [ ('new', 'tryton-new', _('New'), _('Create a new record'), 'sig_new'), ('save', 'tryton-save', _('Save'), _('Save this record'), 'sig_save'), ('switch', 'tryton-fullscreen', _('Switch'), _('Switch view'), 'sig_switch'), ('reload', 'tryton-refresh', _('_Reload'), _('Reload'), 'sig_reload'), (None,) * 5, ('previous', 'tryton-go-previous', _('Previous'), _('Previous Record'), 'sig_previous'), ('next', 'tryton-go-next', _('Next'), _('Next Record'), 'sig_next'), (None,) * 5, ('attach', 'tryton-attachment', _('Attachment(0)'), _('Add an attachment to the record'), 'sig_attach'), ('note', 'tryton-note', _('Note(0)'), _('Add a note to the record'), 'sig_note'), ] menu_def = [ (_('_New'), 'tryton-new', 'sig_new', '<tryton>/Form/New'), (_('_Save'), 'tryton-save', 'sig_save', '<tryton>/Form/Save'), (_('_Switch View'), 'tryton-fullscreen', 'sig_switch', '<tryton>/Form/Switch View'), (_('_Reload/Undo'), 'tryton-refresh', 'sig_reload', '<tryton>/Form/Reload'), (_('_Duplicate'), 'tryton-copy', 'sig_copy', '<tryton>/Form/Duplicate'), (_('_Delete...'), 'tryton-delete', 'sig_remove', '<tryton>/Form/Delete'), (None,) * 4, (_('_Previous'), 'tryton-go-previous', 'sig_previous', '<tryton>/Form/Previous'), (_('_Next'), 'tryton-go-next', 'sig_next', '<tryton>/Form/Next'), (_('_Search'), 'tryton-find', 'sig_search', '<tryton>/Form/Search'), (_('View _Logs...'), None, 'sig_logs', None), (_('Show revisions...'), 'tryton-clock', 'revision', None), (None,) * 4, (_('_Close Tab'), 'tryton-close', 'sig_win_close', '<tryton>/Form/Close'), (None,) * 4, (_('A_ttachments...'), 'tryton-attachment', 'sig_attach', '<tryton>/Form/Attachments'), (_('_Notes...'), 'tryton-note', 'sig_note', '<tryton>/Form/Notes'), (_('_Actions...'), 'tryton-executable', 'sig_action', '<tryton>/Form/Actions'), (_('_Relate...'), 'tryton-go-jump', 'sig_relate', '<tryton>/Form/Relate'), (None,) * 4, (_('_Report...'), 'tryton-print-open', 'sig_print_open', '<tryton>/Form/Report'), (_('_E-Mail...'), 'tryton-print-email', 'sig_print_email', '<tryton>/Form/Email'), (_('_Print...'), 'tryton-print', 'sig_print', '<tryton>/Form/Print'), (None,) * 4, (_('_Export Data...'), 'tryton-save-as', 'sig_save_as', '<tryton>/Form/Export Data'), (_('_Import Data...'), None, 'sig_import', '<tryton>/Form/Import Data'), ] def __init__(self, model, res_id=False, domain=None, order=None, mode=None, view_ids=None, context=None, name=False, limit=None, search_value=None, tab_domain=None, context_model=None): super(Form, self).__init__() if not mode: mode = ['tree', 'form'] if domain is None: domain = [] if view_ids is None: view_ids = [] self.model = model self.res_id = res_id self.domain = domain self.mode = mode self.context = context self.view_ids = view_ids self.dialogs = [] self.screen = Screen(self.model, mode=mode, context=context, view_ids=view_ids, domain=domain, limit=limit, order=order, search_value=search_value, tab_domain=tab_domain, context_model=context_model) self.screen.widget.show() if not name: self.name = self.screen.current_view.title else: self.name = name if self.model not in common.MODELHISTORY: self.menu_def = self.menu_def[:] # Remove callback to revision self.menu_def[11] = (self.menu_def[11][:2] + (None,) + self.menu_def[11][3:]) self.create_tabcontent() self.url_entry = url_entry = gtk.Entry() url_entry.show() url_entry.set_editable(False) style = url_entry.get_style() url_entry.modify_bg(gtk.STATE_ACTIVE, style.bg[gtk.STATE_INSENSITIVE]) self.widget.pack_start(url_entry, False, False) self.set_buttons_sensitive() self.screen.signal_connect(self, 'record-message', self._record_message) self.screen.signal_connect(self, 'record-modified', lambda *a: gobject.idle_add(self._record_modified, *a)) self.screen.signal_connect(self, 'record-saved', self._record_saved) self.screen.signal_connect(self, 'attachment-count', self._attachment_count) self.screen.signal_connect(self, 'unread-note', self._unread_note) if res_id not in (None, False): if isinstance(res_id, (int, long)): res_id = [res_id] self.screen.load(res_id) else: if self.screen.current_view.view_type == 'form': self.sig_new(None, autosave=False) if self.screen.current_view.view_type \ in ('tree', 'graph', 'calendar'): self.screen.search_filter() self.update_revision() def get_toolbars(self): try: return RPCExecute('model', self.model, 'view_toolbar_get', context=self.screen.context) except RPCException: return {} def widget_get(self): return self.screen.widget def __eq__(self, value): if not value: return False if not isinstance(value, Form): return False return (self.model == value.model and self.res_id == value.res_id and self.domain == value.domain and self.mode == value.mode and self.view_ids == value.view_ids and self.screen.context == value.screen.context and self.name == value.name and self.screen.limit == value.screen.limit and self.screen.search_value == value.screen.search_value) def destroy(self): self.screen.destroy() def sig_attach(self, widget=None): record = self.screen.current_record if not record or record.id < 0: return Attachment(record, lambda: self.update_attachment_count(reload=True)) def update_attachment_count(self, reload=False): record = self.screen.current_record if record: attachment_count = record.get_attachment_count(reload=reload) else: attachment_count = 0 self._attachment_count(None, attachment_count) def _attachment_count(self, widget, signal_data): label = _('Attachment(%d)') % signal_data self.buttons['attach'].set_label(label) if signal_data: self.buttons['attach'].set_stock_id('tryton-attachment-hi') else: self.buttons['attach'].set_stock_id('tryton-attachment') record = self.screen.current_record self.buttons['attach'].props.sensitive = bool( record.id >= 0 if record else False) def sig_note(self, widget=None): record = self.screen.current_record if not record or record.id < 0: return Note(record, lambda: self.update_unread_note(reload=True)) def update_unread_note(self, reload=False): record = self.screen.current_record if record: unread = record.get_unread_note(reload=reload) else: unread = 0 self._unread_note(None, unread) def _unread_note(self, widget, signal_data): label = _('Note(%d)') % signal_data self.buttons['note'].set_label(label) if signal_data: self.buttons['note'].set_stock_id('tryton-note-hi') else: self.buttons['note'].set_stock_id('tryton-note') record = self.screen.current_record if not record or record.id < 0: sensitive = False else: sensitive = True self.buttons['note'].props.sensitive = sensitive def sig_switch(self, widget=None): if not self.modified_save(): return self.screen.switch_view() def sig_logs(self, widget=None): current_record = self.screen.current_record if not current_record or current_record.id < 0: self.message_info( _('You have to select one record.'), gtk.MESSAGE_INFO) return False fields = [ ('id', _('ID:')), ('create_uid.rec_name', _('Creation User:'******'create_date', _('Creation Date:')), ('write_uid.rec_name', _('Latest Modification by:')), ('write_date', _('Latest Modification Date:')), ] try: res = RPCExecute('model', self.model, 'read', [current_record.id], [x[0] for x in fields], context=self.screen.context) except RPCException: return date_format = self.screen.context.get('date_format', '%x') datetime_format = date_format + ' %X.%f' message_str = '' for line in res: for (key, val) in fields: value = str(line.get(key, False) or '/') if line.get(key, False) \ and key in ('create_date', 'write_date'): date = timezoned_date(line[key]) value = common.datetime_strftime(date, datetime_format) message_str += val + ' ' + value + '\n' message_str += _('Model:') + ' ' + self.model message(message_str) return True def revision(self, widget=None): if not self.modified_save(): return current_id = (self.screen.current_record.id if self.screen.current_record else None) try: revisions = RPCExecute('model', self.model, 'history_revisions', [r.id for r in self.screen.selected_records]) except RPCException: return revision = self.screen.context.get('_datetime') format_ = self.screen.context.get('date_format', '%x') format_ += ' %X.%f' revision = Revision(revisions, revision, format_).run() # Prevent too old revision in form view if (self.screen.current_view.view_type == 'form' and revision and revision < revisions[-1][0]): revision = revisions[-1][0] if revision != self.screen.context.get('_datetime'): self.screen.clear() # Update root group context that will be propagated self.screen.group._context['_datetime'] = revision if self.screen.current_view.view_type != 'form': self.screen.search_filter( self.screen.screen_container.get_text()) else: # Test if record exist in revisions self.screen.load([current_id]) self.screen.display(set_cursor=True) self.update_revision() def update_revision(self): revision = self.screen.context.get('_datetime') if revision: format_ = self.screen.context.get('date_format', '%x') format_ += ' %X.%f' revision = datetime_strftime(revision, format_) self.title.set_label('%s @ %s' % (self.name, revision)) else: self.title.set_label(self.name) self.set_buttons_sensitive(revision) def set_buttons_sensitive(self, revision=None): if not revision: access = common.MODELACCESS[self.model] self.buttons['new'].props.sensitive = access['create'] self.buttons['save'].props.sensitive = ( access['create'] or access['write']) else: for button in ['new', 'save']: self.buttons[button].props.sensitive = False def sig_remove(self, widget=None): if not common.MODELACCESS[self.model]['delete']: return if self.screen.current_view.view_type == 'form': msg = _('Are you sure to remove this record?') else: msg = _('Are you sure to remove those records?') if sur(msg): if not self.screen.remove(delete=True, force_remove=True): self.message_info(_('Records not removed.'), gtk.MESSAGE_ERROR) else: self.message_info(_('Records removed.'), gtk.MESSAGE_INFO) def sig_import(self, widget=None): WinImport(self.model, self.screen.context) def sig_save_as(self, widget=None): export = WinExport(self.model, [r.id for r in self.screen.selected_records], context=self.screen.context) for name in self.screen.current_view.get_fields(): export.sel_field(name) def sig_new(self, widget=None, autosave=True): if not common.MODELACCESS[self.model]['create']: return if autosave: if not self.modified_save(): return self.screen.new() self.message_info() self.activate_save() def sig_copy(self, widget=None): if not common.MODELACCESS[self.model]['create']: return if not self.modified_save(): return if self.screen.copy(): self.message_info(_('Working now on the duplicated record(s).'), gtk.MESSAGE_INFO) def sig_save(self, widget=None): if widget: # Called from button so we must save the tree state self.screen.save_tree_state() if not (common.MODELACCESS[self.model]['write'] or common.MODELACCESS[self.model]['create']): return if self.screen.save_current(): self.message_info(_('Record saved.'), gtk.MESSAGE_INFO) return True else: self.message_info(self.screen.invalid_message(), gtk.MESSAGE_ERROR) return False def sig_previous(self, widget=None): if not self.modified_save(): return self.screen.display_prev() self.message_info() self.activate_save() def sig_next(self, widget=None): if not self.modified_save(): return self.screen.display_next() self.message_info() self.activate_save() def sig_reload(self, test_modified=True): if test_modified: if not self.modified_save(): return False else: self.screen.save_tree_state(store=False) self.screen.cancel_current() set_cursor = False record_id = (self.screen.current_record.id if self.screen.current_record else None) if self.screen.current_view.view_type != 'form': self.screen.search_filter(self.screen.screen_container.get_text()) for record in self.screen.group: if record.id == record_id: self.screen.current_record = record set_cursor = True break self.screen.display(set_cursor=set_cursor) self.message_info() self.activate_save() return True def sig_action(self, widget): if self.buttons['action'].props.sensitive: self.buttons['action'].props.active = True def sig_print(self, widget): if self.buttons['print'].props.sensitive: self.buttons['print'].props.active = True def sig_print_open(self, widget): if self.buttons['open'].props.sensitive: self.buttons['open'].props.active = True def sig_print_email(self, widget): if self.buttons['email'].props.sensitive: self.buttons['email'].props.active = True def sig_relate(self, widget): if self.buttons['relate'].props.sensitive: self.buttons['relate'].props.active = True def sig_search(self, widget): search_container = self.screen.screen_container if hasattr(search_container, 'search_entry'): search_container.search_entry.grab_focus() def action_popup(self, widget): button, = widget.get_children() button.grab_focus() menu = widget._menu if not widget.props.active: menu.popdown() return def menu_position(menu): parent = widget.get_toplevel() parent_x, parent_y = parent.window.get_origin() widget_allocation = widget.get_allocation() return ( widget_allocation.x + parent_x, widget_allocation.y + widget_allocation.height + parent_y, False ) menu.show_all() menu.popup(None, None, menu_position, 0, 0) def _record_message(self, screen, signal_data): name = '_' if signal_data[0]: name = str(signal_data[0]) for button_id in ('print', 'relate', 'email', 'open', 'save', 'attach'): button = self.buttons[button_id] can_be_sensitive = getattr(button, '_can_be_sensitive', True) button.props.sensitive = (bool(signal_data[0]) and can_be_sensitive) button_switch = self.buttons['switch'] button_switch.props.sensitive = self.screen.number_of_views > 1 msg = name + ' / ' + str(signal_data[1]) if signal_data[1] < signal_data[2]: msg += _(' of ') + str(signal_data[2]) self.status_label.set_text(msg) self.message_info() self.activate_save() self.url_entry.set_text(self.screen.get_url()) def _record_modified(self, screen, signal_data): # As it is called via idle_add, the form could have been destroyed in # the meantime. if self.widget_get().props.window: self.activate_save() def _record_saved(self, screen, signal_data): self.activate_save() self.update_attachment_count() def modified_save(self): self.screen.save_tree_state() self.screen.current_view.set_value() if self.screen.modified(): value = sur_3b( _('This record has been modified\n' 'do you want to save it?')) if value == 'ok': return self.sig_save(None) if value == 'ko': return self.sig_reload(test_modified=False) return False return True def sig_close(self, widget=None): for dialog in self.dialogs[:]: dialog.destroy() return self.modified_save() def _action(self, action, atype): action = action.copy() if not self.screen.save_current(): return record_id = (self.screen.current_record.id if self.screen.current_record else None) record_ids = [r.id for r in self.screen.selected_records] action = Action.evaluate(action, atype, self.screen.current_record) data = { 'model': self.screen.model_name, 'id': record_id, 'ids': record_ids, } Action._exec_action(action, data, self.screen.context) def activate_save(self): self.buttons['save'].props.sensitive = self.screen.modified() def sig_win_close(self, widget): Main.get_main().sig_win_close(widget) def create_toolbar(self, toolbars): gtktoolbar = super(Form, self).create_toolbar(toolbars) attach_btn = self.buttons['attach'] attach_btn.drag_dest_set(gtk.DEST_DEFAULT_ALL, [ ('text/uri-list', 0, 0), ], gtk.gdk.ACTION_MOVE) attach_btn.connect('drag_data_received', self.attach_drag_data_received) iconstock = { 'print': 'tryton-print', 'action': 'tryton-executable', 'relate': 'tryton-go-jump', 'email': 'tryton-print-email', 'open': 'tryton-print-open', } for action_type, special_action, action_name, tooltip in ( ('action', 'action', _('Action'), _('Launch action')), ('relate', 'relate', _('Relate'), _('Open related records')), (None,) * 4, ('print', 'open', _('Report'), _('Open report')), ('print', 'email', _('E-Mail'), _('E-Mail report')), ('print', 'print', _('Print'), _('Print report')), ): if action_type is not None: tbutton = gtk.ToggleToolButton(iconstock.get(special_action)) tbutton.set_label(action_name) tbutton._menu = self._create_popup_menu(tbutton, action_type, toolbars[action_type], special_action) tbutton.connect('toggled', self.action_popup) self.tooltips.set_tip(tbutton, tooltip) self.buttons[special_action] = tbutton if action_type != 'action': tbutton._can_be_sensitive = bool( tbutton._menu.get_children()) else: tbutton = gtk.SeparatorToolItem() gtktoolbar.insert(tbutton, -1) return gtktoolbar def _create_popup_menu(self, widget, keyword, actions, special_action): menu = gtk.Menu() menu.connect('deactivate', self._popup_menu_hide, widget) widget.connect('toggled', self._update_popup, menu, special_action) for action in actions: new_action = action.copy() if special_action == 'print': new_action['direct_print'] = True elif special_action == 'email': new_action['email_print'] = True action_name = action['name'] if '_' not in action_name: action_name = '_' + action_name menuitem = gtk.MenuItem(action_name) menuitem.set_use_underline(True) menuitem.connect('activate', self._popup_menu_selected, widget, new_action, keyword) menu.add(menuitem) return menu def _popup_menu_selected(self, menuitem, togglebutton, action, keyword): event = gtk.get_current_event() allow_similar = False if (event.state & gtk.gdk.CONTROL_MASK or event.state & gtk.gdk.MOD1_MASK): allow_similar = True with Window(hide_current=True, allow_similar=allow_similar): self._action(action, keyword) togglebutton.props.active = False def _popup_menu_hide(self, menuitem, togglebutton): togglebutton.props.active = False def _update_popup(self, tbutton, menu, keyword): assert keyword in ['print','action','relate','email','open'] for item in menu.get_children(): if (getattr(item, '_update_action', False) or isinstance(item, gtk.SeparatorMenuItem)): menu.remove(item) buttons = [button for button in self.screen.get_buttons() if keyword in button.attrs.get('keywords', 'action').split(',')] if buttons: menu.add(gtk.SeparatorMenuItem()) for button in buttons: menuitem = gtk.ImageMenuItem(button.attrs.get('icon')) menuitem.set_label('_' + button.attrs.get('string', _('Unknown'))) menuitem.set_use_underline(True) menuitem.connect('activate', lambda m, attrs: self.screen.button(attrs), button.attrs) menuitem._update_action = True menu.add(menuitem) if keyword == 'action': menu.add(gtk.SeparatorMenuItem()) for plugin in plugins.MODULES: for name, func in plugin.get_plugins(self.model): menuitem = gtk.MenuItem('_' + name) menuitem.set_use_underline(True) menuitem.connect('activate', lambda m, func: func({ 'model': self.model, 'ids': [r.id for r in self.screen.selected_records], 'id': (self.screen.current_record.id if self.screen.current_record else None), }), func) menuitem._update_action = True menu.add(menuitem) def set_cursor(self): if self.screen: self.screen.set_cursor(reset_view=False) def attach_drag_data_received(self, widget, context, x, y, selection, info, timestamp): record = self.screen.current_record if not record or record.id < 0: return win_attach = Attachment(record, lambda: self.update_attachment_count(reload=True)) if info == 0: for uri in selection.data.splitlines(): # Win32 cut&paste terminates the list with a NULL character if not uri or uri == '\0': continue win_attach.add_uri(uri)
class One2Many(Widget): expand = True def __init__(self, view, attrs): super(One2Many, self).__init__(view, attrs) self.widget = gtk.Frame() self.widget.set_shadow_type(gtk.SHADOW_NONE) self.widget.get_accessible().set_name(attrs.get('string', '')) vbox = gtk.VBox(homogeneous=False, spacing=2) self.widget.add(vbox) self._readonly = True self._required = False self._position = 0 self._length = 0 self.title_box = hbox = gtk.HBox(homogeneous=False, spacing=0) hbox.set_border_width(2) self.title = gtk.Label(attrs.get('string', '')) self.title.set_alignment(0.0, 0.5) hbox.pack_start(self.title, expand=True, fill=True) hbox.pack_start(gtk.VSeparator(), expand=False, fill=True) tooltips = common.Tooltips() self.focus_out = True self.wid_completion = None if attrs.get('add_remove'): self.wid_text = PlaceholderEntry() self.wid_text.set_placeholder_text(_('Search')) self.wid_text.set_property('width_chars', 13) self.wid_text.connect('focus-out-event', lambda *a: self._focus_out()) hbox.pack_start(self.wid_text, expand=True, fill=True) if int(self.attrs.get('completion', 1)): access = common.MODELACCESS[attrs['relation']] self.wid_completion = get_completion( search=access['read'] and access['write'], create=attrs.get('create', True) and access['create']) self.wid_completion.connect('match-selected', self._completion_match_selected) self.wid_completion.connect('action-activated', self._completion_action_activated) self.wid_text.set_completion(self.wid_completion) self.wid_text.connect('changed', self._update_completion) self.but_add = gtk.Button() tooltips.set_tip(self.but_add, _('Add existing record')) self.but_add.connect('clicked', self._sig_add) img_add = gtk.Image() img_add.set_from_stock('tryton-list-add', gtk.ICON_SIZE_SMALL_TOOLBAR) img_add.set_alignment(0.5, 0.5) self.but_add.add(img_add) self.but_add.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_add, expand=False, fill=False) self.but_remove = gtk.Button() tooltips.set_tip(self.but_remove, _('Remove selected record')) self.but_remove.connect('clicked', self._sig_remove, True) img_remove = gtk.Image() img_remove.set_from_stock('tryton-list-remove', gtk.ICON_SIZE_SMALL_TOOLBAR) img_remove.set_alignment(0.5, 0.5) self.but_remove.add(img_remove) self.but_remove.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_remove, expand=False, fill=False) hbox.pack_start(gtk.VSeparator(), expand=False, fill=True) self.but_new = gtk.Button() tooltips.set_tip(self.but_new, _('Create a new record <F3>')) self.but_new.connect('clicked', self._sig_new) img_new = gtk.Image() img_new.set_from_stock('tryton-new', gtk.ICON_SIZE_SMALL_TOOLBAR) img_new.set_alignment(0.5, 0.5) self.but_new.add(img_new) self.but_new.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_new, expand=False, fill=False) self.but_open = gtk.Button() tooltips.set_tip(self.but_open, _('Edit selected record <F2>')) self.but_open.connect('clicked', self._sig_edit) img_open = gtk.Image() img_open.set_from_stock('tryton-open', gtk.ICON_SIZE_SMALL_TOOLBAR) img_open.set_alignment(0.5, 0.5) self.but_open.add(img_open) self.but_open.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_open, expand=False, fill=False) self.but_del = gtk.Button() tooltips.set_tip(self.but_del, _('Delete selected record <Del>')) self.but_del.connect('clicked', self._sig_remove, False) img_del = gtk.Image() img_del.set_from_stock('tryton-delete', gtk.ICON_SIZE_SMALL_TOOLBAR) img_del.set_alignment(0.5, 0.5) self.but_del.add(img_del) self.but_del.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_del, expand=False, fill=False) self.but_undel = gtk.Button() tooltips.set_tip(self.but_undel, _('Undelete selected record <Ins>')) self.but_undel.connect('clicked', self._sig_undelete) img_undel = gtk.Image() img_undel.set_from_stock('tryton-undo', gtk.ICON_SIZE_SMALL_TOOLBAR) img_undel.set_alignment(0.5, 0.5) self.but_undel.add(img_undel) self.but_undel.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_undel, expand=False, fill=False) hbox.pack_start(gtk.VSeparator(), expand=False, fill=True) self.but_pre = gtk.Button() tooltips.set_tip(self.but_pre, _('Previous')) self.but_pre.connect('clicked', self._sig_previous) img_pre = gtk.Image() img_pre.set_from_stock('tryton-go-previous', gtk.ICON_SIZE_SMALL_TOOLBAR) img_pre.set_alignment(0.5, 0.5) self.but_pre.add(img_pre) self.but_pre.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_pre, expand=False, fill=False) self.label = gtk.Label('(0,0)') hbox.pack_start(self.label, expand=False, fill=False) self.but_next = gtk.Button() tooltips.set_tip(self.but_next, _('Next')) self.but_next.connect('clicked', self._sig_next) img_next = gtk.Image() img_next.set_from_stock('tryton-go-next', gtk.ICON_SIZE_SMALL_TOOLBAR) img_next.set_alignment(0.5, 0.5) self.but_next.add(img_next) self.but_next.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_next, expand=False, fill=False) hbox.pack_start(gtk.VSeparator(), expand=False, fill=True) but_switch = gtk.Button() tooltips.set_tip(but_switch, _('Switch')) but_switch.connect('clicked', self.switch_view) img_switch = gtk.Image() img_switch.set_from_stock('tryton-fullscreen', gtk.ICON_SIZE_SMALL_TOOLBAR) img_switch.set_alignment(0.5, 0.5) but_switch.add(img_switch) but_switch.set_relief(gtk.RELIEF_NONE) hbox.pack_start(but_switch, expand=False, fill=False) if attrs.get('add_remove'): hbox.set_focus_chain([self.wid_text]) else: hbox.set_focus_chain([]) tooltips.enable() frame = gtk.Frame() frame.add(hbox) frame.set_shadow_type(gtk.SHADOW_OUT) vbox.pack_start(frame, expand=False, fill=True) self.screen = Screen(attrs['relation'], mode=attrs.get('mode', 'tree,form').split(','), view_ids=attrs.get('view_ids', '').split(','), views_preload=attrs.get('views', {}), row_activate=self._on_activate, exclude_field=attrs.get('relation_field', None)) self.screen.pre_validate = bool(int(attrs.get('pre_validate', 0))) self.screen.signal_connect(self, 'record-message', self._sig_label) vbox.pack_start(self.screen.widget, expand=True, fill=True) self.screen.widget.connect('key_press_event', self.on_keypress) if self.attrs.get('add_remove'): self.wid_text.connect('key_press_event', self.on_keypress) but_switch.props.sensitive = self.screen.number_of_views > 1 def on_keypress(self, widget, event): if (event.keyval == gtk.keysyms.F3) \ and self.but_new.get_property('sensitive'): self._sig_new(widget) return True if event.keyval == gtk.keysyms.F2 \ and widget == self.screen.widget: self._sig_edit(widget) return True if (event.keyval in (gtk.keysyms.Delete, gtk.keysyms.KP_Delete) and widget == self.screen.widget and self.but_del.get_property('sensitive')): self._sig_remove(widget) return True if event.keyval == gtk.keysyms.Insert and widget == self.screen.widget: self._sig_undelete(widget) return True if self.attrs.get('add_remove'): editable = self.wid_text.get_editable() activate_keys = [gtk.keysyms.Tab, gtk.keysyms.ISO_Left_Tab] if not self.wid_completion: activate_keys.append(gtk.keysyms.Return) if (widget == self.wid_text and event.keyval in activate_keys and editable and self.wid_text.get_text()): self._sig_add() self.wid_text.grab_focus() return False def destroy(self): self.screen.destroy() def _on_activate(self): self._sig_edit() def switch_view(self, widget): self.screen.switch_view() @property def modified(self): return self.screen.current_view.modified def _readonly_set(self, value): self._readonly = value self._set_button_sensitive() self._set_label_state() def _required_set(self, value): self._required = value self._set_label_state() def _set_label_state(self): attrlist = common.get_label_attributes(self._readonly, self._required) self.title.set_attributes(attrlist) def _set_button_sensitive(self): access = common.MODELACCESS[self.screen.model_name] if self.record and self.field: field_size = self.record.expr_eval(self.attrs.get('size')) o2m_size = len(self.field.get_eval(self.record)) size_limit = (field_size is not None and o2m_size >= field_size >= 0) else: o2m_size = None size_limit = False self.but_new.set_sensitive( bool(not self._readonly and self.attrs.get('create', True) and not size_limit and access['create'])) self.but_del.set_sensitive( bool(not self._readonly and self.attrs.get('delete', True) and self._position and access['delete'])) self.but_undel.set_sensitive( bool(not self._readonly and not size_limit and self._position)) self.but_open.set_sensitive(bool(self._position and access['read'])) self.but_next.set_sensitive( bool(self._position and self._position < self._length)) self.but_pre.set_sensitive(bool(self._position and self._position > 1)) if self.attrs.get('add_remove'): self.but_add.set_sensitive( bool(not self._readonly and not size_limit and access['write'] and access['read'])) self.but_remove.set_sensitive( bool(not self._readonly and self._position and access['write'] and access['read'])) self.wid_text.set_sensitive(self.but_add.get_sensitive()) # New button must be added to focus chain to allow keyboard only # creation when there is no existing record on form view. focus_chain = self.title_box.get_focus_chain() or [] if o2m_size == 0 and self.screen.current_view.view_type == 'form': if self.but_new not in focus_chain: focus_chain.append(self.but_new) else: if self.but_new in focus_chain: focus_chain.remove(self.but_new) self.title_box.set_focus_chain(focus_chain) def _validate(self): self.view.set_value() record = self.screen.current_record if record: fields = self.screen.current_view.get_fields() if not record.validate(fields): self.screen.display(set_cursor=True) return False if self.screen.pre_validate and not record.pre_validate(): return False return True def _sig_new(self, *args): if not common.MODELACCESS[self.screen.model_name]['create']: return if not self._validate(): return if self.attrs.get('product'): self._new_product() else: self._new_single() def _new_single(self): ctx = {} ctx.update(self.field.context_get(self.record)) sequence = None for view in self.screen.views: if view.view_type == 'tree': sequence = view.attributes.get('sequence') if sequence: break def update_sequence(): if sequence: self.screen.group.set_sequence(field=sequence) if self.screen.current_view.editable: self.screen.new() self.screen.current_view.widget.set_sensitive(True) update_sequence() else: field_size = self.record.expr_eval(self.attrs.get('size')) or -1 field_size -= len(self.field.get_eval(self.record)) + 1 WinForm(self.screen, lambda a: update_sequence(), new=True, many=field_size, context=ctx, title=self.attrs.get('string')) def _new_product(self): fields = self.attrs['product'].split(',') product = {} first = self.screen.new(default=False) default = first.default_get() first.set_default(default) def search_set(*args): if not fields: return make_product() field = self.screen.group.fields[fields.pop()] relation = field.attrs.get('relation') if not relation: search_set() domain = field.domain_get(first) context = field.context_get(first) def callback(result): if result: product[field.name] = result win_search = WinSearch(relation, callback, sel_multi=True, context=context, domain=domain, title=self.attrs.get('string')) win_search.win.connect('destroy', search_set) win_search.screen.search_filter() win_search.show() def make_product(first=first): if not product: self.screen.group.remove(first, remove=True) return fields = product.keys() for values in itertools.product(*product.values()): if first: record = first first = None else: record = self.screen.new(default=False) default_value = default.copy() for field, value in zip(fields, values): id_, rec_name = value default_value[field] = id_ default_value[field + '.rec_name'] = rec_name record.set_default(default_value) search_set() def _sig_edit(self, widget=None): if not common.MODELACCESS[self.screen.model_name]['read']: return if not self._validate(): return record = self.screen.current_record if record: WinForm(self.screen, lambda a: None, title=self.attrs.get('string')) def _sig_next(self, widget): if not self._validate(): return self.screen.display_next() def _sig_previous(self, widget): if not self._validate(): return self.screen.display_prev() def _sig_remove(self, widget, remove=False): access = common.MODELACCESS[self.screen.model_name] if remove: if not access['write'] or not access['read']: return else: if not access['delete']: return self.screen.remove(remove=remove) def _sig_undelete(self, button): self.screen.unremove() def _sig_add(self, *args): if not self.focus_out: return access = common.MODELACCESS[self.screen.model_name] if not access['write'] or not access['read']: return self.view.set_value() domain = self.field.domain_get(self.record) context = self.field.context_get(self.record) domain = [domain, self.record.expr_eval(self.attrs.get('add_remove'))] removed_ids = self.field.get_removed_ids(self.record) domain = ['OR', domain, ('id', 'in', removed_ids)] text = self.wid_text.get_text().decode('utf-8') self.focus_out = False sequence = None if self.screen.current_view.view_type == 'tree': sequence = self.screen.current_view.attributes.get('sequence') def callback(result): self.focus_out = True if result: ids = [x[0] for x in result] self.screen.load(ids, modified=True) self.screen.display(res_id=ids[0]) if sequence: self.screen.group.set_sequence(field=sequence) self.screen.set_cursor() self.wid_text.set_text('') win = WinSearch(self.attrs['relation'], callback, sel_multi=True, context=context, domain=domain, view_ids=self.attrs.get('view_ids', '').split(','), views_preload=self.attrs.get('views', {}), new=self.but_new.get_property('sensitive'), title=self.attrs.get('string')) win.screen.search_filter(quote(text)) win.show() def _sig_label(self, screen, signal_data): self._position = signal_data[0] self._length = signal_data[1] if self._position >= 1: name = str(self._position) else: name = '_' line = '(%s/%s)' % (name, self._length) self.label.set_text(line) self._set_button_sensitive() def display(self, record, field): super(One2Many, self).display(record, field) self._set_button_sensitive() if field is None: self.screen.new_group() self.screen.current_record = None self.screen.parent = None self.screen.display() return False new_group = field.get_client(record) if id(self.screen.group) != id(new_group): self.screen.group = new_group if (self.screen.current_view.view_type == 'tree') \ and self.screen.current_view.editable: self.screen.current_record = None domain = [] size_limit = None if record: domain = field.domain_get(record) size_limit = record.expr_eval(self.attrs.get('size')) if self.screen.domain != domain: self.screen.domain = domain self.screen.size_limit = size_limit self.screen.display() return True def set_value(self, record, field): self.screen.current_view.set_value() if self.screen.modified(): # TODO check if required record.modified_fields.setdefault(field.name) record.signal('record-modified') return True def _completion_match_selected(self, completion, model, iter_): record_id, = model.get(iter_, 1) self.screen.load([record_id], modified=True) self.wid_text.set_text('') self.wid_text.grab_focus() completion_model = self.wid_completion.get_model() completion_model.clear() completion_model.search_text = self.wid_text.get_text() return True def _update_completion(self, widget): if self._readonly: return if not self.record: return model = self.attrs['relation'] domain = self.field.domain_get(self.record) domain = [domain, self.record.expr_eval(self.attrs.get('add_remove'))] removed_ids = self.field.get_removed_ids(self.record) domain = ['OR', domain, ('id', 'in', removed_ids)] update_completion(self.wid_text, self.record, self.field, model, domain=domain) def _completion_action_activated(self, completion, index): if index == 0: self._sig_add() self.wid_text.grab_focus() elif index == 1: self._sig_new()
def __init__(self, attrs=None, context=None): super(Action, self).__init__() self.name = attrs['name'] self.context = context or {} try: self.action = RPCExecute('model', 'ir.action.act_window', 'get', self.name) except RPCException: raise view_ids = None self.action['view_mode'] = None if self.action.get('views', []): view_ids = [x[0] for x in self.action['views']] self.action['view_mode'] = [x[1] for x in self.action['views']] elif self.action.get('view_id', False): view_ids = [self.action['view_id'][0]] if 'view_mode' in attrs: self.action['view_mode'] = attrs['view_mode'] self.action.setdefault('pyson_domain', '[]') self.context.update(rpc.CONTEXT) self.context['_user'] = rpc._USER self.context.update( PYSONDecoder(self.context).decode( self.action.get('pyson_context', '{}'))) eval_ctx = self.context.copy() self.context.update( PYSONDecoder(eval_ctx).decode( self.action.get('pyson_context', '{}'))) self.domain = [] self.update_domain([]) search_context = self.context.copy() search_context['context'] = self.context search_context['_user'] = rpc._USER search_value = PYSONDecoder(search_context).decode( self.action['pyson_search_value'] or '{}') self.widget = gtk.Frame() self.widget.set_border_width(0) vbox = gtk.VBox(homogeneous=False, spacing=3) self.widget.add(vbox) self.title = gtk.Label() self.widget.set_label_widget(self.title) self.widget.set_label_align(0.0, 0.5) self.widget.show_all() self.screen = Screen(self.action['res_model'], mode=self.action['view_mode'], context=self.context, view_ids=view_ids, domain=self.domain, search_value=search_value, row_activate=self.row_activate) vbox.pack_start(self.screen.widget, expand=True, fill=True) self.screen.signal_connect(self, 'record-message', self._active_changed) if attrs.get('string'): self.title.set_text(attrs['string']) else: self.title.set_text(self.action['name']) self.widget.set_size_request(int(attrs.get('width', -1)), int(attrs.get('height', -1))) self.screen.search_filter()
class One2Many(WidgetInterface): def __init__(self, field_name, model_name, attrs=None): super(One2Many, self).__init__(field_name, model_name, attrs=attrs) self.widget = gtk.VBox(homogeneous=False, spacing=2) self._readonly = True hbox = gtk.HBox(homogeneous=False, spacing=0) hbox.set_border_width(2) label = gtk.Label(attrs.get('string', '')) label.set_alignment(0.0, 0.5) hbox.pack_start(label, expand=True, fill=True) hbox.pack_start(gtk.VSeparator(), expand=False, fill=True) tooltips = common.Tooltips() if attrs.get('add_remove'): self.wid_text = gtk.Entry() self.wid_text.set_property('width_chars', 13) self.wid_text.connect('activate', self._sig_activate) self.wid_text.connect('focus-out-event', self._focus_out) hbox.pack_start(self.wid_text, expand=True, fill=True) self.but_add = gtk.Button() tooltips.set_tip(self.but_add, _('Add')) self.but_add.connect('clicked', self._sig_add) img_add = gtk.Image() img_add.set_from_stock('tryton-list-add', gtk.ICON_SIZE_SMALL_TOOLBAR) img_add.set_alignment(0.5, 0.5) self.but_add.add(img_add) self.but_add.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_add, expand=False, fill=False) self.but_remove = gtk.Button() tooltips.set_tip(self.but_remove, _('Remove <Del>')) self.but_remove.connect('clicked', self._sig_remove, True) img_remove = gtk.Image() img_remove.set_from_stock('tryton-list-remove', gtk.ICON_SIZE_SMALL_TOOLBAR) img_remove.set_alignment(0.5, 0.5) self.but_remove.add(img_remove) self.but_remove.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_remove, expand=False, fill=False) hbox.pack_start(gtk.VSeparator(), expand=False, fill=True) self.but_new = gtk.Button() tooltips.set_tip(self.but_new, _('Create a new record <F3>')) self.but_new.connect('clicked', self._sig_new) img_new = gtk.Image() img_new.set_from_stock('tryton-new', gtk.ICON_SIZE_SMALL_TOOLBAR) img_new.set_alignment(0.5, 0.5) self.but_new.add(img_new) self.but_new.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_new, expand=False, fill=False) self.but_open = gtk.Button() tooltips.set_tip(self.but_open, _('Edit selected record <F2>')) self.but_open.connect('clicked', self._sig_edit) img_open = gtk.Image() img_open.set_from_stock('tryton-open', gtk.ICON_SIZE_SMALL_TOOLBAR) img_open.set_alignment(0.5, 0.5) self.but_open.add(img_open) self.but_open.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_open, expand=False, fill=False) self.but_del = gtk.Button() tooltips.set_tip(self.but_del, _('Delete selected record <Del>')) self.but_del.connect('clicked', self._sig_remove, False) img_del = gtk.Image() img_del.set_from_stock('tryton-delete', gtk.ICON_SIZE_SMALL_TOOLBAR) img_del.set_alignment(0.5, 0.5) self.but_del.add(img_del) self.but_del.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_del, expand=False, fill=False) self.but_undel = gtk.Button() tooltips.set_tip(self.but_undel, _('Undelete selected record <Ins>')) self.but_undel.connect('clicked', self._sig_undelete) img_undel = gtk.Image() img_undel.set_from_stock('tryton-undo', gtk.ICON_SIZE_SMALL_TOOLBAR) img_undel.set_alignment(0.5, 0.5) self.but_undel.add(img_undel) self.but_undel.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_undel, expand=False, fill=False) hbox.pack_start(gtk.VSeparator(), expand=False, fill=True) self.but_pre = gtk.Button() tooltips.set_tip(self.but_pre, _('Previous')) self.but_pre.connect('clicked', self._sig_previous) img_pre = gtk.Image() img_pre.set_from_stock('tryton-go-previous', gtk.ICON_SIZE_SMALL_TOOLBAR) img_pre.set_alignment(0.5, 0.5) self.but_pre.add(img_pre) self.but_pre.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_pre, expand=False, fill=False) self.label = gtk.Label('(0,0)') hbox.pack_start(self.label, expand=False, fill=False) self.but_next = gtk.Button() tooltips.set_tip(self.but_next, _('Next')) self.but_next.connect('clicked', self._sig_next) img_next = gtk.Image() img_next.set_from_stock('tryton-go-next', gtk.ICON_SIZE_SMALL_TOOLBAR) img_next.set_alignment(0.5, 0.5) self.but_next.add(img_next) self.but_next.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_next, expand=False, fill=False) hbox.pack_start(gtk.VSeparator(), expand=False, fill=True) but_switch = gtk.Button() tooltips.set_tip(but_switch, _('Switch')) but_switch.connect('clicked', self.switch_view) img_switch = gtk.Image() img_switch.set_from_stock('tryton-fullscreen', gtk.ICON_SIZE_SMALL_TOOLBAR) img_switch.set_alignment(0.5, 0.5) but_switch.add(img_switch) but_switch.set_relief(gtk.RELIEF_NONE) hbox.pack_start(but_switch, expand=False, fill=False) if attrs.get('add_remove'): hbox.set_focus_chain([self.wid_text]) else: hbox.set_focus_chain([]) tooltips.enable() frame = gtk.Frame() frame.add(hbox) frame.set_shadow_type(gtk.SHADOW_OUT) self.widget.pack_start(frame, expand=False, fill=True) self.screen = Screen(attrs['relation'], mode=attrs.get('mode', 'tree,form').split(','), view_ids=attrs.get('view_ids', '').split(','), views_preload=attrs.get('views', {}), row_activate=self._on_activate, exclude_field=attrs.get('relation_field', None)) self.screen.signal_connect(self, 'record-message', self._sig_label) self.widget.pack_start(self.screen.widget, expand=True, fill=True) self.screen.widget.connect('key_press_event', self.on_keypress) if self.attrs.get('add_remove'): self.wid_text.connect('key_press_event', self.on_keypress) def _color_widget(self): if hasattr(self.screen.current_view, 'widget_tree'): return self.screen.current_view.widget_tree return super(One2Many, self)._color_widget() def grab_focus(self): return self.screen.widget.grab_focus() def on_keypress(self, widget, event): if (event.keyval == gtk.keysyms.F3) \ and self.but_new.get_property('sensitive'): self._sig_new(widget) return False if event.keyval == gtk.keysyms.F2 \ and widget == self.screen.widget: self._sig_edit(widget) return False if event.keyval in (gtk.keysyms.Delete, gtk.keysyms.KP_Delete) \ and widget == self.screen.widget: self._sig_remove(widget) return False if event.keyval == gtk.keysyms.Insert and widget == self.screen.widget: self._sig_undelete(widget) return False def destroy(self): self.screen.destroy() def _on_activate(self): self._sig_edit() def switch_view(self, widget): self.screen.switch_view() self.color_set(self.color_name) @property def modified(self): return self.screen.current_view.modified def color_set(self, name): super(One2Many, self).color_set(name) widget = self._color_widget() # if the style to apply is different from readonly then insensitive # cellrenderers should use the default insensitive color if name != 'readonly': widget.modify_text(gtk.STATE_INSENSITIVE, self.colors['text_color_insensitive']) def _readonly_set(self, value): self._readonly = value self.but_new.set_sensitive(not value) self.but_del.set_sensitive(not value) self.but_undel.set_sensitive(not value) if self.attrs.get('add_remove'): self.wid_text.set_sensitive(not value) self.but_add.set_sensitive(not value) self.but_remove.set_sensitive(not value) def _sig_new(self, widget): self.view.set_value() record = self.screen.current_record if record: fields = self.screen.current_view.get_fields() if not record.validate(fields): self.screen.display() return ctx = {} ctx.update(self.field.context_get(self.record)) sequence = None if self.screen.current_view.view_type == 'tree': sequence = self.screen.current_view.widget_tree.sequence def update_sequence(): if sequence: self.screen.group.set_sequence(field=sequence) if (self.screen.current_view.view_type == 'form') \ or self.screen.editable_get(): self.screen.new(context=ctx) self.screen.current_view.widget.set_sensitive(True) update_sequence() else: WinForm(self.screen, lambda a: update_sequence(), new=True, many=True, context=ctx) def _sig_edit(self, widget=None): self.view.set_value() record = self.screen.current_record if record: fields = self.screen.current_view.get_fields() if not record.validate(fields): self.screen.display() return WinForm(self.screen, lambda a: None) def _sig_next(self, widget): self.view.set_value() record = self.screen.current_record if record: fields = self.screen.current_view.get_fields() if not record.validate(fields): self.screen.display() return self.screen.display_next() def _sig_previous(self, widget): self.view.set_value() record = self.screen.current_record if record: fields = self.screen.current_view.get_fields() if not record.validate(fields): self.screen.display() return self.screen.display_prev() def _sig_remove(self, widget, remove=False): self.screen.remove(remove=remove) def _sig_undelete(self, button): self.screen.unremove() def _sig_activate(self, *args): self._sig_add() self.wid_text.grab_focus() def _focus_out(self, *args): if self.wid_text.get_text(): self._sig_add() def _sig_add(self, *args): self.view.set_value() domain = self.field.domain_get(self.record) context = self.field.context_get(self.record) domain = domain[:] domain.extend(self.record.expr_eval(self.attrs.get('add_remove'))) removed_ids = self.field.get_removed_ids(self.record) try: if self.wid_text.get_text(): dom = [('rec_name', 'ilike', '%' + self.wid_text.get_text() + '%'), ['OR', domain, ('id', 'in', removed_ids)]] else: dom = ['OR', domain, ('id', 'in', removed_ids)] ids = RPCExecute('model', self.attrs['relation'], 'search', dom, 0, CONFIG['client.limit'], None, context=context) except RPCException: return False def callback(result): if result: ids = [x[0] for x in result] self.screen.load(ids, modified=True) self.screen.display(res_id=ids[0]) self.screen.set_cursor() self.wid_text.set_text('') if len(ids) != 1: WinSearch(self.attrs['relation'], callback, sel_multi=True, ids=ids, context=context, domain=domain, view_ids=self.attrs.get('view_ids', '').split(','), views_preload=self.attrs.get('views', {})) else: callback([(i, None) for i in ids]) def _sig_label(self, screen, signal_data): name = '_' if signal_data[0] >= 1: name = str(signal_data[0]) self.but_open.set_sensitive(True) self.but_del.set_sensitive(not self._readonly) if self.attrs.get('add_remove'): self.but_remove.set_sensitive(not self._readonly) if signal_data[0] < signal_data[1]: self.but_next.set_sensitive(True) else: self.but_next.set_sensitive(False) if signal_data[0] > 1: self.but_pre.set_sensitive(True) else: self.but_pre.set_sensitive(False) self.but_del.set_sensitive(not self._readonly) self.but_undel.set_sensitive(not self._readonly) else: self.but_open.set_sensitive(False) self.but_del.set_sensitive(False) self.but_undel.set_sensitive(False) self.but_next.set_sensitive(False) self.but_pre.set_sensitive(False) if self.attrs.get('add_remove'): self.but_remove.set_sensitive(False) line = '(%s/%s)' % (name, signal_data[1]) self.label.set_text(line) def display(self, record, field): super(One2Many, self).display(record, field) if field is None: self.screen.new_group() self.screen.current_record = None self.screen.parent = True self.screen.display() return False new_group = field.get_client(record) if id(self.screen.group) != id(new_group): self.screen.group = new_group if (self.screen.current_view.view_type == 'tree') \ and self.screen.editable_get(): self.screen.current_record = None readonly = False domain = [] if record: readonly = field.get_state_attrs(record).get('readonly', False) domain = field.domain_get(record) if self.screen.domain != domain: self.screen.domain = domain self.screen.group.readonly = readonly self.screen.display() return True def set_value(self, record, field): self.screen.save_tree_state() self.screen.current_view.set_value() if self.screen.modified(): # TODO check if required record.modified_fields.setdefault(field.name) record.signal('record-modified') return True
class Many2ManySelection(Widget, SelectionMixin): expand = True def __init__(self, view, attrs): super(Many2ManySelection, self).__init__(view, attrs) self.widget = gtk.VBox(homogeneous=False, spacing=5) hbox = gtk.HBox(homogeneous=False, spacing=0) hbox.set_border_width(2) label = gtk.Label(attrs.get("string", "")) label.set_alignment(0.0, 0.5) hbox.pack_start(label, expand=True, fill=True) frame = gtk.Frame() frame.add(hbox) frame.set_shadow_type(gtk.SHADOW_OUT) self.widget.pack_start(frame, expand=False, fill=True) self.screen = Screen( attrs["relation"], view_ids=attrs.get("view_ids", "").split(","), mode=["tree"], views_preload=attrs.get("views", {}), ) self.screen.new_group() self.treeview = self.screen.current_view.treeview self.treeview.get_selection().connect("changed", self.changed) self.treeview.connect("focus-out-event", lambda *a: self._focus_out()) self.treeview.connect("button-press-event", self.button_press_event) self.treeview.connect("key-press-event", self.key_press_event) self.widget.pack_start(self.screen.widget, expand=True, fill=True) self.nullable_widget = False self.init_selection() @property def modified(self): if self.record and self.field: group = set(r.id for r in self.field.get_client(self.record)) value = set(self.get_value()) return value != group return False def changed(self, selection): def focus_out(): if self.widget.props.window: self._focus_out() # Must be deferred because it triggers a display of the form gobject.idle_add(focus_out) def button_press_event(self, treeview, event): # grab focus because it doesn't whith CONTROL MASK treeview.grab_focus() if event.button == 1: event.state ^= gtk.gdk.CONTROL_MASK def key_press_event(self, treeview, event): if event.keyval in MOVEMENT_KEYS: event.state ^= gtk.gdk.CONTROL_MASK def get_value(self): return [r.id for r in self.screen.selected_records] def set_value(self, record, field): field.set_client(record, self.get_value()) def display(self, record, field): selection = self.treeview.get_selection() selection.handler_block_by_func(self.changed) try: self.update_selection(record, field) super(Many2ManySelection, self).display(record, field) if field is None: self.screen.clear() self.screen.current_record = None self.screen.parent = None else: self.screen.parent = record current_ids = [r.id for r in self.screen.group] new_ids = [s[0] for s in self.selection] if current_ids != new_ids: self.screen.clear() self.screen.load(new_ids) group = field.get_client(record) nodes = [[r.id] for r in group if r not in group.record_removed and r not in group.record_deleted] selection.unselect_all() self.screen.current_view.select_nodes(nodes) self.screen.display() finally: selection.handler_unblock_by_func(self.changed)
def load(record): if isinstance(record, (int, long)): screen = Screen(model) screen.load([record]) record = screen.current_record return record
class WinSearch(NoModal): def __init__(self, model, callback, sel_multi=True, context=None, domain=None, view_ids=None, views_preload=None, new=True, title=''): NoModal.__init__(self) if views_preload is None: views_preload = {} self.domain = domain or [] self.context = context or {} self.sel_multi = sel_multi self.callback = callback self.title = title self.win = gtk.Dialog(_('Search'), self.parent, gtk.DIALOG_DESTROY_WITH_PARENT) self.win.set_icon(GNUHEALTH_ICON) self.win.set_default_response(gtk.RESPONSE_APPLY) self.win.connect('response', self.response) self.accel_group = gtk.AccelGroup() self.win.add_accel_group(self.accel_group) self.but_cancel = self.win.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL) self.but_find = self.win.add_button(gtk.STOCK_FIND, gtk.RESPONSE_APPLY) if new and common.MODELACCESS[model]['create']: self.but_new = self.win.add_button(gtk.STOCK_NEW, gtk.RESPONSE_ACCEPT) self.but_new.set_accel_path('<tryton>/Form/New', self.accel_group) self.but_ok = self.win.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK) self.but_ok.add_accelerator('clicked', self.accel_group, gtk.keysyms.Return, gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE) hbox = gtk.HBox() hbox.show() self.win.vbox.pack_start(hbox, expand=False, fill=True) self.win.vbox.pack_start(gtk.HSeparator(), expand=False, fill=True) scrollwindow = gtk.ScrolledWindow() scrollwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) self.win.vbox.pack_start(scrollwindow, expand=True, fill=True) self.screen = Screen(model, domain=domain, mode=['tree'], context=context, view_ids=view_ids, views_preload=views_preload, row_activate=self.sig_activate) self.view = self.screen.current_view self.view.unset_editable() # Prevent to set tree_state self.screen.tree_states_done.add(id(self.view)) sel = self.view.treeview.get_selection() self.win.set_title(_('Search %s') % self.title) if not sel_multi: sel.set_mode(gtk.SELECTION_SINGLE) else: sel.set_mode(gtk.SELECTION_MULTIPLE) viewport = gtk.Viewport() viewport.set_shadow_type(gtk.SHADOW_NONE) viewport.add(self.screen.widget) self.screen.widget.show() viewport.show() scrollwindow.add(viewport) scrollwindow.show() self.model_name = model self.win.set_default_size(700, 500) self.register() sensible_allocation = self.sensible_widget.get_allocation() self.win.set_default_size(int(sensible_allocation.width * 0.9), int(sensible_allocation.height * 0.9)) def sig_activate(self, *args): self.view.treeview.emit_stop_by_name('row_activated') self.win.response(gtk.RESPONSE_OK) return True def destroy(self): self.screen.destroy() self.win.destroy() NoModal.destroy(self) def show(self): self.win.show() common.center_window(self.win, self.parent, self.sensible_widget) def hide(self): self.win.hide() def response(self, win, response_id): res = None if response_id == gtk.RESPONSE_OK: res = [r.id for r in self.screen.selected_records] elif response_id == gtk.RESPONSE_APPLY: self.screen.search_filter(self.screen.screen_container.get_text()) return elif response_id == gtk.RESPONSE_ACCEPT: screen = Screen(self.model_name, domain=self.domain, context=self.context, mode=['form']) def callback(result): if result and screen.save_current(): record = screen.current_record res = [(record.id, record.value.get('rec_name', ''))] self.callback(res) else: self.callback(None) self.destroy() WinForm(screen, callback, new=True, title=self.title) return if res: group = self.screen.group res = [(id_, group.get(id_).value.get('rec_name', '')) for id_ in res] self.callback(res) self.destroy()
class One2Many(Widget): expand = True def __init__(self, view, attrs): super(One2Many, self).__init__(view, attrs) self.widget = gtk.Frame() self.widget.set_shadow_type(gtk.SHADOW_NONE) self.widget.get_accessible().set_name(attrs.get("string", "")) vbox = gtk.VBox(homogeneous=False, spacing=2) self.widget.add(vbox) self._readonly = True self._position = 0 self._length = 0 self.title_box = hbox = gtk.HBox(homogeneous=False, spacing=0) hbox.set_border_width(2) label = gtk.Label(attrs.get("string", "")) label.set_alignment(0.0, 0.5) hbox.pack_start(label, expand=True, fill=True) hbox.pack_start(gtk.VSeparator(), expand=False, fill=True) tooltips = common.Tooltips() self.focus_out = True self.wid_completion = None if attrs.get("add_remove"): self.wid_text = PlaceholderEntry() self.wid_text.set_placeholder_text(_("Search")) self.wid_text.set_property("width_chars", 13) self.wid_text.connect("focus-out-event", lambda *a: self._focus_out()) hbox.pack_start(self.wid_text, expand=True, fill=True) if int(self.attrs.get("completion", 1)): access = common.MODELACCESS[attrs["relation"]] self.wid_completion = get_completion( search=access["read"] and access["write"], create=attrs.get("create", True) and access["create"] ) self.wid_completion.connect("match-selected", self._completion_match_selected) self.wid_completion.connect("action-activated", self._completion_action_activated) self.wid_text.set_completion(self.wid_completion) self.wid_text.connect("changed", self._update_completion) self.but_add = gtk.Button() tooltips.set_tip(self.but_add, _("Add existing record")) self.but_add.connect("clicked", self._sig_add) img_add = gtk.Image() img_add.set_from_stock("tryton-list-add", gtk.ICON_SIZE_SMALL_TOOLBAR) img_add.set_alignment(0.5, 0.5) self.but_add.add(img_add) self.but_add.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_add, expand=False, fill=False) self.but_remove = gtk.Button() tooltips.set_tip(self.but_remove, _("Remove selected record")) self.but_remove.connect("clicked", self._sig_remove, True) img_remove = gtk.Image() img_remove.set_from_stock("tryton-list-remove", gtk.ICON_SIZE_SMALL_TOOLBAR) img_remove.set_alignment(0.5, 0.5) self.but_remove.add(img_remove) self.but_remove.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_remove, expand=False, fill=False) hbox.pack_start(gtk.VSeparator(), expand=False, fill=True) self.but_new = gtk.Button() tooltips.set_tip(self.but_new, _("Create a new record <F3>")) self.but_new.connect("clicked", self._sig_new) img_new = gtk.Image() img_new.set_from_stock("tryton-new", gtk.ICON_SIZE_SMALL_TOOLBAR) img_new.set_alignment(0.5, 0.5) self.but_new.add(img_new) self.but_new.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_new, expand=False, fill=False) self.but_open = gtk.Button() tooltips.set_tip(self.but_open, _("Edit selected record <F2>")) self.but_open.connect("clicked", self._sig_edit) img_open = gtk.Image() img_open.set_from_stock("tryton-open", gtk.ICON_SIZE_SMALL_TOOLBAR) img_open.set_alignment(0.5, 0.5) self.but_open.add(img_open) self.but_open.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_open, expand=False, fill=False) self.but_del = gtk.Button() tooltips.set_tip(self.but_del, _("Delete selected record <Del>")) self.but_del.connect("clicked", self._sig_remove, False) img_del = gtk.Image() img_del.set_from_stock("tryton-delete", gtk.ICON_SIZE_SMALL_TOOLBAR) img_del.set_alignment(0.5, 0.5) self.but_del.add(img_del) self.but_del.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_del, expand=False, fill=False) self.but_undel = gtk.Button() tooltips.set_tip(self.but_undel, _("Undelete selected record <Ins>")) self.but_undel.connect("clicked", self._sig_undelete) img_undel = gtk.Image() img_undel.set_from_stock("tryton-undo", gtk.ICON_SIZE_SMALL_TOOLBAR) img_undel.set_alignment(0.5, 0.5) self.but_undel.add(img_undel) self.but_undel.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_undel, expand=False, fill=False) hbox.pack_start(gtk.VSeparator(), expand=False, fill=True) self.but_pre = gtk.Button() tooltips.set_tip(self.but_pre, _("Previous")) self.but_pre.connect("clicked", self._sig_previous) img_pre = gtk.Image() img_pre.set_from_stock("tryton-go-previous", gtk.ICON_SIZE_SMALL_TOOLBAR) img_pre.set_alignment(0.5, 0.5) self.but_pre.add(img_pre) self.but_pre.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_pre, expand=False, fill=False) self.label = gtk.Label("(0,0)") hbox.pack_start(self.label, expand=False, fill=False) self.but_next = gtk.Button() tooltips.set_tip(self.but_next, _("Next")) self.but_next.connect("clicked", self._sig_next) img_next = gtk.Image() img_next.set_from_stock("tryton-go-next", gtk.ICON_SIZE_SMALL_TOOLBAR) img_next.set_alignment(0.5, 0.5) self.but_next.add(img_next) self.but_next.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_next, expand=False, fill=False) hbox.pack_start(gtk.VSeparator(), expand=False, fill=True) but_switch = gtk.Button() tooltips.set_tip(but_switch, _("Switch")) but_switch.connect("clicked", self.switch_view) img_switch = gtk.Image() img_switch.set_from_stock("tryton-fullscreen", gtk.ICON_SIZE_SMALL_TOOLBAR) img_switch.set_alignment(0.5, 0.5) but_switch.add(img_switch) but_switch.set_relief(gtk.RELIEF_NONE) hbox.pack_start(but_switch, expand=False, fill=False) if attrs.get("add_remove"): hbox.set_focus_chain([self.wid_text]) else: hbox.set_focus_chain([]) tooltips.enable() frame = gtk.Frame() frame.add(hbox) if attrs.get("expand_toolbar"): frame.set_shadow_type(gtk.SHADOW_NONE) else: frame.set_shadow_type(gtk.SHADOW_OUT) vbox.pack_start(frame, expand=False, fill=True) self.screen = Screen( attrs["relation"], mode=attrs.get("mode", "tree,form").split(","), view_ids=attrs.get("view_ids", "").split(","), views_preload=attrs.get("views", {}), row_activate=self._on_activate, readonly=self.attrs.get("readonly", False), exclude_field=attrs.get("relation_field", None), ) self.screen.pre_validate = bool(int(attrs.get("pre_validate", 0))) self.screen.signal_connect(self, "record-message", self._sig_label) if self.attrs.get("group"): self.screen.signal_connect( self, "current-record-changed", lambda screen, _: gobject.idle_add(self.group_sync, screen, screen.current_record), ) vbox.pack_start(self.screen.widget, expand=True, fill=True) self.screen.widget.connect("key_press_event", self.on_keypress) if self.attrs.get("add_remove"): self.wid_text.connect("key_press_event", self.on_keypress) but_switch.props.sensitive = self.screen.number_of_views > 1 def _color_widget(self): if hasattr(self.screen.current_view, "treeview"): return self.screen.current_view.treeview return super(One2Many, self)._color_widget() def on_keypress(self, widget, event): if (event.keyval == gtk.keysyms.F3) and self.but_new.get_property("sensitive"): self._sig_new(widget) return True if event.keyval == gtk.keysyms.F2 and widget == self.screen.widget: self._sig_edit(widget) return True if ( event.keyval in (gtk.keysyms.Delete, gtk.keysyms.KP_Delete) and widget == self.screen.widget and self.but_del.get_property("sensitive") ): self._sig_remove(widget) return True if event.keyval == gtk.keysyms.Insert and widget == self.screen.widget: self._sig_undelete(widget) return True if self.attrs.get("add_remove"): editable = self.wid_text.get_editable() activate_keys = [gtk.keysyms.Tab, gtk.keysyms.ISO_Left_Tab] if not self.wid_completion: activate_keys.append(gtk.keysyms.Return) if widget == self.wid_text and event.keyval in activate_keys and editable and self.wid_text.get_text(): self._sig_add() self.wid_text.grab_focus() return False def destroy(self): self.screen.destroy() def _on_activate(self): self._sig_edit() def switch_view(self, widget): self.screen.switch_view() self.color_set(self.color_name) @property def modified(self): return self.screen.current_view.modified def color_set(self, name): super(One2Many, self).color_set(name) widget = self._color_widget() # if the style to apply is different from readonly then insensitive # cellrenderers should use the default insensitive color if name != "readonly": widget.modify_text(gtk.STATE_INSENSITIVE, self.colors["text_color_insensitive"]) def _readonly_set(self, value): self._readonly = value self._set_button_sensitive() def _set_button_sensitive(self): access = common.MODELACCESS[self.screen.model_name] if self.record and self.field: field_size = self.record.expr_eval(self.attrs.get("size")) o2m_size = len(self.field.get_eval(self.record)) size_limit = field_size is not None and o2m_size >= field_size >= 0 else: o2m_size = None size_limit = False has_form = "form" in (x.view_type for x in self.screen.views) or "form" in self.screen.view_to_load self.but_new.set_sensitive( bool( not self._readonly and self.attrs.get("create", True) and not size_limit and access["create"] and (has_form or self.screen.current_view.editable) ) ) self.but_del.set_sensitive( bool(not self._readonly and self.attrs.get("delete", True) and self._position and access["delete"]) ) self.but_undel.set_sensitive(bool(not self._readonly and not size_limit and self._position)) self.but_open.set_sensitive(bool(self._position and access["read"] and has_form)) self.but_next.set_sensitive(bool(self._position and self._position < self._length)) self.but_pre.set_sensitive(bool(self._position and self._position > 1)) if self.attrs.get("add_remove"): self.but_add.set_sensitive( bool(not self._readonly and not size_limit and access["write"] and access["read"]) ) self.but_remove.set_sensitive( bool(not self._readonly and self._position and access["write"] and access["read"]) ) self.wid_text.set_sensitive(self.but_add.get_sensitive()) # New button must be added to focus chain to allow keyboard only # creation when there is no existing record on form view. focus_chain = self.title_box.get_focus_chain() or [] if o2m_size == 0 and self.screen.current_view.view_type == "form": if self.but_new not in focus_chain: focus_chain.append(self.but_new) else: if self.but_new in focus_chain: focus_chain.remove(self.but_new) self.title_box.set_focus_chain(focus_chain) def _validate(self): record = self.screen.current_record if record: fields = self.screen.current_view.get_fields() if not record.validate(fields): self.screen.display(set_cursor=True) return False if self.screen.pre_validate and not record.pre_validate(): return False return True def _sig_new(self, *args): if not common.MODELACCESS[self.screen.model_name]["create"]: return if not self._validate(): return if self.attrs.get("product"): self._new_product() else: self._new_single() def _new_single(self): ctx = {} ctx.update(self.field.context_get(self.record)) sequence = None for view in self.screen.views: if view.view_type == "tree": sequence = view.attributes.get("sequence") if sequence: break def update_sequence(): if sequence: self.screen.group.set_sequence(field=sequence) for widget in [self] + self.view.widgets[self.field_name]: if ( (self.attrs.get("group") and widget.attrs.get("group") != self.attrs["group"]) or not widget.visible or not hasattr(widget, "screen") ): continue if ( widget.screen.current_view.view_type == "form" or widget.screen.current_view.editable and not widget.screen.editable_open_get() ): widget.screen.new() widget.screen.current_view.widget.set_sensitive(True) update_sequence() break else: field_size = self.record.expr_eval(self.attrs.get("size")) or -1 field_size -= len(self.field.get_eval(self.record)) + 1 WinForm(self.screen, lambda a: update_sequence(), new=True, many=field_size, context=ctx) def _new_product(self): fields = self.attrs["product"].split(",") product = {} first = self.screen.new(default=False) default = first.default_get() first.set_default(default) def search_set(*args): if not fields: return make_product() field = self.screen.group.fields[fields.pop()] relation = field.attrs.get("relation") if not relation: search_set() domain = field.domain_get(first) context = field.context_get(first) def callback(result): if result: product[field.name] = result win_search = WinSearch(relation, callback, sel_multi=True, context=context, domain=domain) win_search.win.connect("destroy", search_set) win_search.screen.search_filter() win_search.show() def make_product(first=first): if not product: self.screen.group.remove(first, remove=True) return fields = product.keys() for values in itertools.product(*product.values()): if first: record = first first = None else: record = self.screen.new(default=False) default_value = default.copy() for field, value in zip(fields, values): id_, rec_name = value default_value[field] = id_ default_value[field + ".rec_name"] = rec_name record.set_default(default_value) search_set() def _sig_edit(self, widget=None): if not self.but_open.props.sensitive: return if not self._validate(): return record = self.screen.current_record if record: WinForm(self.screen, lambda a: None) def _sig_next(self, widget): if not self._validate(): return self.screen.display_next() def _sig_previous(self, widget): if not self._validate(): return self.screen.display_prev() def _sig_remove(self, widget, remove=False): access = common.MODELACCESS[self.screen.model_name] if remove: if not access["write"] or not access["read"]: return else: if not access["delete"]: return self.screen.remove(remove=remove) def _sig_undelete(self, button): self.screen.unremove() def _sig_add(self, *args): if not self.focus_out: return access = common.MODELACCESS[self.screen.model_name] if not access["write"] or not access["read"]: return domain = self.field.domain_get(self.record) context = self.field.context_get(self.record) domain = [domain, self.record.expr_eval(self.attrs.get("add_remove"))] removed_ids = self.field.get_removed_ids(self.record) domain = ["OR", domain, ("id", "in", removed_ids)] text = self.wid_text.get_text().decode("utf-8") self.focus_out = False sequence = None if self.screen.current_view.view_type == "tree": sequence = self.screen.current_view.attributes.get("sequence") def callback(result): self.focus_out = True if result: ids = [x[0] for x in result] self.screen.load(ids, modified=True) self.screen.display(res_id=ids[0]) if sequence: self.screen.group.set_sequence(field=sequence) self.screen.set_cursor() self.wid_text.set_text("") win = WinSearch( self.attrs["relation"], callback, sel_multi=True, context=context, domain=domain, view_ids=self.attrs.get("view_ids", "").split(","), views_preload=self.attrs.get("views", {}), new=self.but_new.get_property("sensitive"), ) win.screen.search_filter(quote(text)) win.show() def _sig_label(self, screen, signal_data): self._position = signal_data[0] self._length = signal_data[1] if self._position >= 1: name = str(self._position) else: name = "_" line = "(%s/%s)" % (name, self._length) self.label.set_text(line) self._set_button_sensitive() def group_sync(self, screen, current_record): if not self.screen.widget.props.window: return if not self.view or not self.view.widgets: return if self.attrs.get("mode") == "form": return if screen.current_record != current_record: return def is_compatbile(screen, record): return not ( screen.current_view.view_type == "form" and record is not None and screen.model_name != record.model_name ) current_record = self.screen.current_record to_sync = [] for widget in self.view.widgets[self.field_name]: if widget == self or widget.attrs.get("group") != self.attrs["group"] or not hasattr(widget, "screen"): continue if widget.screen.current_record == current_record: continue record = current_record if not is_compatbile(widget.screen, record): record = None if not widget._validate(): def go_previous(): record = widget.screen.current_record if not is_compatbile(screen, record): record = None screen.current_record = record screen.display() gobject.idle_add(go_previous) return to_sync.append((widget, record)) for widget, record in to_sync: if ( widget.screen.current_view.view_type == "form" and record is not None and widget.screen.group.model_name == record.group.model_name ): fields = dict((name, field.attrs) for name, field in widget.screen.group.fields.iteritems()) record.group.load_fields(fields) widget.screen.current_record = record widget.display(widget.record, widget.field) def display(self, record, field): super(One2Many, self).display(record, field) self._set_button_sensitive() if field is None: self.screen.new_group() self.screen.current_record = None self.screen.parent = None self.screen.display() return False new_group = field.get_client(record) if self.attrs.get("group") and self.attrs.get("mode") == "form": if self.screen.current_record is None: self.invisible_set(True) elif id(self.screen.group) != id(new_group): self.screen.group = new_group if (self.screen.current_view.view_type == "tree") and self.screen.current_view.editable: self.screen.current_record = None readonly = False domain = [] size_limit = None if record: readonly = field.get_state_attrs(record).get("readonly", False) domain = field.domain_get(record) size_limit = record.expr_eval(self.attrs.get("size")) if self.screen.get_domain() != domain: self.screen.domain = domain self.screen.group.readonly = readonly self.screen.size_limit = size_limit self.screen.display() return True def set_value(self, record, field): if ( self.screen.current_view.view_type == "form" and self.attrs.get("group") and self.screen.model_name != record.model_name ): return True self.screen.current_view.set_value() if self.screen.modified(): # TODO check if required record.modified_fields.setdefault(field.name) record.signal("record-modified") return True def _completion_match_selected(self, completion, model, iter_): record_id, = model.get(iter_, 1) self.screen.load([record_id], modified=True) self.wid_text.set_text("") self.wid_text.grab_focus() completion_model = self.wid_completion.get_model() completion_model.clear() completion_model.search_text = self.wid_text.get_text() return True def _update_completion(self, widget): if self._readonly: return if not self.record: return model = self.attrs["relation"] domain = self.field.domain_get(self.record) domain = [domain, self.record.expr_eval(self.attrs.get("add_remove"))] removed_ids = self.field.get_removed_ids(self.record) domain = ["OR", domain, ("id", "in", removed_ids)] update_completion(self.wid_text, self.record, self.field, model, domain=domain) def _completion_action_activated(self, completion, index): if index == 0: self._sig_add() self.wid_text.grab_focus() elif index == 1: self._sig_new()
class Form(SignalEvent, TabContent): "Form" def __init__(self, model, res_id=None, name='', **attributes): super(Form, self).__init__() self.model = model self.res_id = res_id self.mode = attributes.get('mode') self.view_ids = attributes.get('view_ids') self.dialogs = [] self.screen = Screen(self.model, **attributes) self.screen.widget.show() self.name = name self.create_tabcontent() self.set_buttons_sensitive() self.screen.signal_connect(self, 'record-message', self._record_message) self.screen.signal_connect( self, 'record-modified', lambda *a: gobject.idle_add(self._record_modified, *a)) self.screen.signal_connect(self, 'record-saved', self._record_saved) self.screen.signal_connect(self, 'attachment-count', self._attachment_count) self.screen.signal_connect(self, 'unread-note', self._unread_note) if res_id not in (None, False): if isinstance(res_id, (int, long)): res_id = [res_id] self.screen.load(res_id) else: if self.screen.current_view.view_type == 'form': self.sig_new(None, autosave=False) if self.screen.current_view.view_type \ in ('tree', 'graph', 'calendar'): self.screen.search_filter() self.update_revision() def get_toolbars(self): try: return RPCExecute('model', self.model, 'view_toolbar_get', context=self.screen.context) except RPCException: return {} def widget_get(self): return self.screen.widget def __eq__(self, value): if not value: return False if not isinstance(value, Form): return False return (self.model == value.model and self.res_id == value.res_id and self.screen.domain == value.screen.domain and self.mode == value.mode and self.view_ids == value.view_ids and self.screen.context == value.screen.context and self.name == value.name and self.screen.limit == value.screen.limit and self.screen.search_value == value.screen.search_value) def destroy(self): super(Form, self).destroy() self.screen.signal_unconnect(self) self.screen.destroy() def sig_attach(self, widget=None): record = self.screen.current_record if not record or record.id < 0: return Attachment(record, lambda: self.update_attachment_count(reload=True)) def update_attachment_count(self, reload=False): record = self.screen.current_record if record: attachment_count = record.get_attachment_count(reload=reload) else: attachment_count = 0 self._attachment_count(None, attachment_count) def _attachment_count(self, widget, signal_data): label = _('Attachment(%d)') % signal_data self.buttons['attach'].set_label(label) if signal_data: self.buttons['attach'].set_stock_id('tryton-attachment-hi') else: self.buttons['attach'].set_stock_id('tryton-attachment') record = self.screen.current_record self.buttons['attach'].props.sensitive = bool( record.id >= 0 if record else False) def sig_note(self, widget=None): record = self.screen.current_record if not record or record.id < 0: return Note(record, lambda: self.update_unread_note(reload=True)) def update_unread_note(self, reload=False): record = self.screen.current_record if record: unread = record.get_unread_note(reload=reload) else: unread = 0 self._unread_note(None, unread) def _unread_note(self, widget, signal_data): label = _('Note(%d)') % signal_data self.buttons['note'].set_label(label) if signal_data: self.buttons['note'].set_stock_id('tryton-note-hi') else: self.buttons['note'].set_stock_id('tryton-note') record = self.screen.current_record if not record or record.id < 0: sensitive = False else: sensitive = True self.buttons['note'].props.sensitive = sensitive def sig_switch(self, widget=None): if not self.modified_save(): return self.screen.switch_view() def sig_logs(self, widget=None): current_record = self.screen.current_record if not current_record or current_record.id < 0: self.message_info(_('You have to select one record.'), gtk.MESSAGE_INFO) return False fields = [ ('id', _('ID:')), ('create_uid.rec_name', _('Creation User:'******'create_date', _('Creation Date:')), ('write_uid.rec_name', _('Latest Modification by:')), ('write_date', _('Latest Modification Date:')), ] try: res = RPCExecute('model', self.model, 'read', [current_record.id], [x[0] for x in fields], context=self.screen.context) except RPCException: return date_format = self.screen.context.get('date_format', '%x') datetime_format = date_format + ' %H:%M:%S.%f' message_str = '' for line in res: for (key, val) in fields: value = str(line.get(key, False) or '/') if line.get(key, False) \ and key in ('create_date', 'write_date'): date = timezoned_date(line[key]) value = common.datetime_strftime(date, datetime_format) message_str += val + ' ' + value + '\n' message_str += _('Model:') + ' ' + self.model message(message_str) return True def sig_revision(self, widget=None): if not self.modified_save(): return current_id = (self.screen.current_record.id if self.screen.current_record else None) try: revisions = RPCExecute( 'model', self.model, 'history_revisions', [r.id for r in self.screen.selected_records]) except RPCException: return revision = self.screen.context.get('_datetime') format_ = self.screen.context.get('date_format', '%x') format_ += ' %H:%M:%S.%f' revision = Revision(revisions, revision, format_).run() # Prevent too old revision in form view if (self.screen.current_view.view_type == 'form' and revision and revision < revisions[-1][0]): revision = revisions[-1][0] if revision != self.screen.context.get('_datetime'): self.screen.clear() # Update root group context that will be propagated self.screen.group._context['_datetime'] = revision if self.screen.current_view.view_type != 'form': self.screen.search_filter( self.screen.screen_container.get_text()) else: # Test if record exist in revisions self.screen.load([current_id]) self.screen.display(set_cursor=True) self.update_revision() def update_revision(self): revision = self.screen.context.get('_datetime') if revision: format_ = self.screen.context.get('date_format', '%x') format_ += ' %H:%M:%S.%f' revision = datetime_strftime(revision, format_) self.title.set_label('%s @ %s' % (self.name, revision)) else: self.title.set_label(self.name) self.set_buttons_sensitive(revision) def set_buttons_sensitive(self, revision=None): if not revision: access = common.MODELACCESS[self.model] for name, sensitive in [ ('new', access['create']), ('save', access['create'] or access['write']), ('remove', access['delete']), ('copy', access['create']), ('import', access['create']), ]: if name in self.buttons: self.buttons[name].props.sensitive = sensitive if name in self.menu_buttons: self.menu_buttons[name].props.sensitive = sensitive else: for name in ['new', 'save', 'remove', 'copy', 'import']: if name in self.buttons: self.buttons[name].props.sensitive = False if name in self.menu_buttons: self.menu_buttons[name].props.sensitive = False def sig_remove(self, widget=None): if not common.MODELACCESS[self.model]['delete']: return if self.screen.current_view.view_type == 'form': msg = _('Are you sure to remove this record?') else: msg = _('Are you sure to remove those records?') if sur(msg): if not self.screen.remove(delete=True, force_remove=True): self.message_info(_('Records not removed.'), gtk.MESSAGE_ERROR) else: self.message_info(_('Records removed.'), gtk.MESSAGE_INFO) self.screen.count_tab_domain() def sig_import(self, widget=None): WinImport(self.model, self.screen.context) def sig_export(self, widget=None): export = WinExport(self.model, [r.id for r in self.screen.selected_records], context=self.screen.context) for name in self.screen.current_view.get_fields(): export.sel_field(name) def sig_new(self, widget=None, autosave=True): if not common.MODELACCESS[self.model]['create']: return if autosave: if not self.modified_save(): return self.screen.new() self.message_info() self.activate_save() def sig_copy(self, widget=None): if not common.MODELACCESS[self.model]['create']: return if not self.modified_save(): return if self.screen.copy(): self.message_info(_('Working now on the duplicated record(s).'), gtk.MESSAGE_INFO) self.screen.count_tab_domain() def sig_save(self, widget=None): if widget: # Called from button so we must save the tree state self.screen.save_tree_state() if not (common.MODELACCESS[self.model]['write'] or common.MODELACCESS[self.model]['create']): return if self.screen.save_current(): self.message_info(_('Record saved.'), gtk.MESSAGE_INFO) self.screen.count_tab_domain() return True else: self.message_info(self.screen.invalid_message(), gtk.MESSAGE_ERROR) return False def sig_previous(self, widget=None): if not self.modified_save(): return self.screen.display_prev() self.message_info() self.activate_save() def sig_next(self, widget=None): if not self.modified_save(): return self.screen.display_next() self.message_info() self.activate_save() def sig_reload(self, test_modified=True): if test_modified: if not self.modified_save(): return False else: self.screen.save_tree_state(store=False) self.screen.cancel_current() set_cursor = False record_id = (self.screen.current_record.id if self.screen.current_record else None) if self.screen.current_view.view_type != 'form': self.screen.search_filter(self.screen.screen_container.get_text()) for record in self.screen.group: if record.id == record_id: self.screen.current_record = record set_cursor = True break self.screen.display(set_cursor=set_cursor) self.message_info() self.activate_save() self.screen.count_tab_domain() return True def sig_action(self, widget): if self.buttons['action'].props.sensitive: self.buttons['action'].props.active = True def sig_print(self, widget): if self.buttons['print'].props.sensitive: self.buttons['print'].props.active = True def sig_print_open(self, widget): if self.buttons['open'].props.sensitive: self.buttons['open'].props.active = True def sig_print_email(self, widget): if self.buttons['email'].props.sensitive: self.buttons['email'].props.active = True def sig_relate(self, widget): if self.buttons['relate'].props.sensitive: self.buttons['relate'].props.active = True def sig_copy_url(self, widget): if self.buttons['copy_url'].props.sensitive: self.buttons['copy_url'].props.active = True def sig_search(self, widget): search_container = self.screen.screen_container if hasattr(search_container, 'search_entry'): search_container.search_entry.grab_focus() def action_popup(self, widget): button, = widget.get_children() button.grab_focus() menu = widget._menu if not widget.props.active: menu.popdown() return def menu_position(menu, data): widget_allocation = widget.get_allocation() if hasattr(widget.window, 'get_root_coords'): x, y = widget.window.get_root_coords(widget_allocation.x, widget_allocation.y) else: x, y = widget.window.get_origin() x += widget_allocation.x y += widget_allocation.y return (x, y + widget_allocation.height, False) menu.show_all() menu.popup(None, None, menu_position, 0, gtk.get_current_event_time(), None) def _record_message(self, screen, signal_data): name = '_' if signal_data[0]: name = str(signal_data[0]) for button_id in ('print', 'relate', 'email', 'open', 'save', 'attach'): button = self.buttons[button_id] can_be_sensitive = getattr(button, '_can_be_sensitive', True) button.props.sensitive = (bool(signal_data[0]) and can_be_sensitive) button_switch = self.buttons['switch'] button_switch.props.sensitive = self.screen.number_of_views > 1 msg = name + ' / ' + str(signal_data[1]) if signal_data[1] < signal_data[2]: msg += _(' of ') + str(signal_data[2]) self.status_label.set_text(msg) self.message_info() self.activate_save() def _record_modified(self, screen, signal_data): # As it is called via idle_add, the form could have been destroyed in # the meantime. if self.widget_get().props.window: self.activate_save() def _record_saved(self, screen, signal_data): self.activate_save() self.update_attachment_count() def modified_save(self): self.screen.save_tree_state() self.screen.current_view.set_value() if self.screen.modified(): value = sur_3b( _('This record has been modified\n' 'do you want to save it?')) if value == 'ok': return self.sig_save(None) if value == 'ko': return self.sig_reload(test_modified=False) return False return True def sig_close(self, widget=None): for dialog in self.dialogs[:]: dialog.destroy() return self.modified_save() def _action(self, action, atype): action = action.copy() if not self.screen.save_current(): return record_id = (self.screen.current_record.id if self.screen.current_record else None) record_ids = [r.id for r in self.screen.selected_records] action = Action.evaluate(action, atype, self.screen.current_record) data = { 'model': self.screen.model_name, 'id': record_id, 'ids': record_ids, } Action._exec_action(action, data, self.screen.context) def activate_save(self): self.buttons['save'].props.sensitive = self.screen.modified() def sig_win_close(self, widget): Main.get_main().sig_win_close(widget) def create_toolbar(self, toolbars): gtktoolbar = super(Form, self).create_toolbar(toolbars) attach_btn = self.buttons['attach'] target_entry = gtk.TargetEntry.new('text/uri-list', 0, 0) attach_btn.drag_dest_set(gtk.DEST_DEFAULT_ALL, [ target_entry, ], gtk.gdk.ACTION_MOVE | gtk.gdk.ACTION_COPY) attach_btn.connect('drag_data_received', self.attach_drag_data_received) iconstock = { 'print': 'tryton-print', 'action': 'tryton-executable', 'relate': 'tryton-go-jump', 'email': 'tryton-print-email', 'open': 'tryton-print-open', } for action_type, special_action, action_name, tooltip in ( ('action', 'action', _('Action'), _('Launch action')), ('relate', 'relate', _('Relate'), _('Open related records')), (None, ) * 4, ('print', 'open', _('Report'), _('Open report')), ('print', 'email', _('E-Mail'), _('E-Mail report')), ('print', 'print', _('Print'), _('Print report')), ): if action_type is not None: tbutton = gtk.ToggleToolButton(iconstock.get(special_action)) tbutton.set_label(action_name) tbutton._menu = self._create_popup_menu( tbutton, action_type, toolbars[action_type], special_action) tbutton.connect('toggled', self.action_popup) self.tooltips.set_tip(tbutton, tooltip) self.buttons[special_action] = tbutton if action_type != 'action': tbutton._can_be_sensitive = bool( tbutton._menu.get_children()) else: tbutton = gtk.SeparatorToolItem() gtktoolbar.insert(tbutton, -1) gtktoolbar.insert(gtk.SeparatorToolItem(), -1) url_button = gtk.ToggleToolButton('tryton-web-browser') url_button.set_label(_('_Copy URL')) url_button.set_use_underline(True) self.tooltips.set_tip(url_button, _('Copy URL into clipboard')) url_button._menu = url_menu = gtk.Menu() url_menuitem = gtk.MenuItem() url_menuitem.connect('activate', self.url_copy) url_menu.add(url_menuitem) url_menu.show_all() url_menu.connect('deactivate', self._popup_menu_hide, url_button) url_button.connect('toggled', self.url_set, url_menuitem) url_button.connect('toggled', self.action_popup) self.buttons['copy_url'] = url_button gtktoolbar.insert(url_button, -1) return gtktoolbar def _create_popup_menu(self, widget, keyword, actions, special_action): menu = gtk.Menu() menu.connect('deactivate', self._popup_menu_hide, widget) widget.connect('toggled', self._update_popup, menu, special_action) for action in actions: new_action = action.copy() if special_action == 'print': new_action['direct_print'] = True elif special_action == 'email': new_action['email_print'] = True action_name = action['name'] if '_' not in action_name: action_name = '_' + action_name menuitem = gtk.MenuItem(action_name) menuitem.set_use_underline(True) menuitem.connect('activate', self._popup_menu_selected, widget, new_action, keyword) menu.add(menuitem) return menu def _popup_menu_selected(self, menuitem, togglebutton, action, keyword): event = gtk.get_current_event() allow_similar = False if (event.state & gtk.gdk.CONTROL_MASK or event.state & gtk.gdk.MOD1_MASK): allow_similar = True with Window(hide_current=True, allow_similar=allow_similar): self._action(action, keyword) togglebutton.props.active = False def _popup_menu_hide(self, menuitem, togglebutton): togglebutton.props.active = False def _update_popup(self, tbutton, menu, keyword): assert keyword in ['print', 'action', 'relate', 'email', 'open'] for item in menu.get_children(): if (getattr(item, '_update_action', False) or isinstance(item, gtk.SeparatorMenuItem)): menu.remove(item) buttons = [ button for button in self.screen.get_buttons() if keyword in button.attrs.get('keywords', 'action').split(',') ] if buttons: menu.add(gtk.SeparatorMenuItem()) for button in buttons: menuitem = gtk.ImageMenuItem() menuitem.set_label('_' + button.attrs.get('string', _('Unknown'))) menuitem.set_use_underline(True) if button.attrs.get('icon'): icon = gtk.Image() icon.set_from_stock(button.attrs['icon'], gtk.ICON_SIZE_MENU) menuitem.set_image(icon) menuitem.connect('activate', lambda m, attrs: self.screen.button(attrs), button.attrs) menuitem._update_action = True menu.add(menuitem) if keyword == 'action': menu.add(gtk.SeparatorMenuItem()) for plugin in plugins.MODULES: for name, func in plugin.get_plugins(self.model): menuitem = gtk.MenuItem('_' + name) menuitem.set_use_underline(True) menuitem.connect( 'activate', lambda m, func: func({ 'model': self.model, 'ids': [r.id for r in self.screen.selected_records], 'id': (self.screen.current_record.id if self.screen.current_record else None), }), func) menuitem._update_action = True menu.add(menuitem) def url_copy(self, menuitem): url = self.screen.get_url(self.name) for selection in [gtk.CLIPBOARD_PRIMARY, gtk.CLIPBOARD_CLIPBOARD]: clipboard = gtk.clipboard_get(selection) clipboard.set_text(url, -1) def url_set(self, button, menuitem): url = self.screen.get_url(self.name) size = 80 if len(url) > size: url = url[:size // 2] + '...' + url[-size // 2:] menuitem.set_label(url) def set_cursor(self): if self.screen: self.screen.set_cursor(reset_view=False) def attach_drag_data_received(self, widget, context, x, y, selection, info, timestamp): record = self.screen.current_record if not record or record.id < 0: return win_attach = Attachment( record, lambda: self.update_attachment_count(reload=True)) if info == 0: for uri in selection.data.splitlines(): # Win32 cut&paste terminates the list with a NULL character if not uri or uri == '\0': continue win_attach.add_uri(uri)
def __init__(self, view, attrs): super(One2Many, self).__init__(view, attrs) self.widget = gtk.Frame() self.widget.set_shadow_type(gtk.SHADOW_NONE) self.widget.get_accessible().set_name(attrs.get("string", "")) vbox = gtk.VBox(homogeneous=False, spacing=2) self.widget.add(vbox) self._readonly = True self._position = 0 self._length = 0 self.title_box = hbox = gtk.HBox(homogeneous=False, spacing=0) hbox.set_border_width(2) label = gtk.Label(attrs.get("string", "")) label.set_alignment(0.0, 0.5) hbox.pack_start(label, expand=True, fill=True) hbox.pack_start(gtk.VSeparator(), expand=False, fill=True) tooltips = common.Tooltips() self.focus_out = True self.wid_completion = None if attrs.get("add_remove"): self.wid_text = PlaceholderEntry() self.wid_text.set_placeholder_text(_("Search")) self.wid_text.set_property("width_chars", 13) self.wid_text.connect("focus-out-event", lambda *a: self._focus_out()) hbox.pack_start(self.wid_text, expand=True, fill=True) if int(self.attrs.get("completion", 1)): access = common.MODELACCESS[attrs["relation"]] self.wid_completion = get_completion( search=access["read"] and access["write"], create=attrs.get("create", True) and access["create"] ) self.wid_completion.connect("match-selected", self._completion_match_selected) self.wid_completion.connect("action-activated", self._completion_action_activated) self.wid_text.set_completion(self.wid_completion) self.wid_text.connect("changed", self._update_completion) self.but_add = gtk.Button() tooltips.set_tip(self.but_add, _("Add existing record")) self.but_add.connect("clicked", self._sig_add) img_add = gtk.Image() img_add.set_from_stock("tryton-list-add", gtk.ICON_SIZE_SMALL_TOOLBAR) img_add.set_alignment(0.5, 0.5) self.but_add.add(img_add) self.but_add.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_add, expand=False, fill=False) self.but_remove = gtk.Button() tooltips.set_tip(self.but_remove, _("Remove selected record")) self.but_remove.connect("clicked", self._sig_remove, True) img_remove = gtk.Image() img_remove.set_from_stock("tryton-list-remove", gtk.ICON_SIZE_SMALL_TOOLBAR) img_remove.set_alignment(0.5, 0.5) self.but_remove.add(img_remove) self.but_remove.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_remove, expand=False, fill=False) hbox.pack_start(gtk.VSeparator(), expand=False, fill=True) self.but_new = gtk.Button() tooltips.set_tip(self.but_new, _("Create a new record <F3>")) self.but_new.connect("clicked", self._sig_new) img_new = gtk.Image() img_new.set_from_stock("tryton-new", gtk.ICON_SIZE_SMALL_TOOLBAR) img_new.set_alignment(0.5, 0.5) self.but_new.add(img_new) self.but_new.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_new, expand=False, fill=False) self.but_open = gtk.Button() tooltips.set_tip(self.but_open, _("Edit selected record <F2>")) self.but_open.connect("clicked", self._sig_edit) img_open = gtk.Image() img_open.set_from_stock("tryton-open", gtk.ICON_SIZE_SMALL_TOOLBAR) img_open.set_alignment(0.5, 0.5) self.but_open.add(img_open) self.but_open.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_open, expand=False, fill=False) self.but_del = gtk.Button() tooltips.set_tip(self.but_del, _("Delete selected record <Del>")) self.but_del.connect("clicked", self._sig_remove, False) img_del = gtk.Image() img_del.set_from_stock("tryton-delete", gtk.ICON_SIZE_SMALL_TOOLBAR) img_del.set_alignment(0.5, 0.5) self.but_del.add(img_del) self.but_del.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_del, expand=False, fill=False) self.but_undel = gtk.Button() tooltips.set_tip(self.but_undel, _("Undelete selected record <Ins>")) self.but_undel.connect("clicked", self._sig_undelete) img_undel = gtk.Image() img_undel.set_from_stock("tryton-undo", gtk.ICON_SIZE_SMALL_TOOLBAR) img_undel.set_alignment(0.5, 0.5) self.but_undel.add(img_undel) self.but_undel.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_undel, expand=False, fill=False) hbox.pack_start(gtk.VSeparator(), expand=False, fill=True) self.but_pre = gtk.Button() tooltips.set_tip(self.but_pre, _("Previous")) self.but_pre.connect("clicked", self._sig_previous) img_pre = gtk.Image() img_pre.set_from_stock("tryton-go-previous", gtk.ICON_SIZE_SMALL_TOOLBAR) img_pre.set_alignment(0.5, 0.5) self.but_pre.add(img_pre) self.but_pre.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_pre, expand=False, fill=False) self.label = gtk.Label("(0,0)") hbox.pack_start(self.label, expand=False, fill=False) self.but_next = gtk.Button() tooltips.set_tip(self.but_next, _("Next")) self.but_next.connect("clicked", self._sig_next) img_next = gtk.Image() img_next.set_from_stock("tryton-go-next", gtk.ICON_SIZE_SMALL_TOOLBAR) img_next.set_alignment(0.5, 0.5) self.but_next.add(img_next) self.but_next.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_next, expand=False, fill=False) hbox.pack_start(gtk.VSeparator(), expand=False, fill=True) but_switch = gtk.Button() tooltips.set_tip(but_switch, _("Switch")) but_switch.connect("clicked", self.switch_view) img_switch = gtk.Image() img_switch.set_from_stock("tryton-fullscreen", gtk.ICON_SIZE_SMALL_TOOLBAR) img_switch.set_alignment(0.5, 0.5) but_switch.add(img_switch) but_switch.set_relief(gtk.RELIEF_NONE) hbox.pack_start(but_switch, expand=False, fill=False) if attrs.get("add_remove"): hbox.set_focus_chain([self.wid_text]) else: hbox.set_focus_chain([]) tooltips.enable() frame = gtk.Frame() frame.add(hbox) if attrs.get("expand_toolbar"): frame.set_shadow_type(gtk.SHADOW_NONE) else: frame.set_shadow_type(gtk.SHADOW_OUT) vbox.pack_start(frame, expand=False, fill=True) self.screen = Screen( attrs["relation"], mode=attrs.get("mode", "tree,form").split(","), view_ids=attrs.get("view_ids", "").split(","), views_preload=attrs.get("views", {}), row_activate=self._on_activate, readonly=self.attrs.get("readonly", False), exclude_field=attrs.get("relation_field", None), ) self.screen.pre_validate = bool(int(attrs.get("pre_validate", 0))) self.screen.signal_connect(self, "record-message", self._sig_label) if self.attrs.get("group"): self.screen.signal_connect( self, "current-record-changed", lambda screen, _: gobject.idle_add(self.group_sync, screen, screen.current_record), ) vbox.pack_start(self.screen.widget, expand=True, fill=True) self.screen.widget.connect("key_press_event", self.on_keypress) if self.attrs.get("add_remove"): self.wid_text.connect("key_press_event", self.on_keypress) but_switch.props.sensitive = self.screen.number_of_views > 1
class WinSearch(NoModal): def __init__(self, model, callback, sel_multi=True, context=None, domain=None, order=None, view_ids=None, views_preload=None, new=True, title='', exclude_field=None): NoModal.__init__(self) if view_ids is None: view_ids = [] if views_preload is None: views_preload = {} self.domain = domain or [] self.context = context or {} self.order = order self.view_ids = view_ids self.views_preload = views_preload self.sel_multi = sel_multi self.callback = callback self.title = title self.exclude_field = exclude_field self.win = Gtk.Dialog( title=_('Search'), transient_for=self.parent, destroy_with_parent=True) Main().add_window(self.win) self.win.set_icon(TRYTON_ICON) self.win.set_position(Gtk.WindowPosition.CENTER_ON_PARENT) self.win.set_default_response(Gtk.ResponseType.APPLY) self.win.connect('response', self.response) self.win.set_default_size(*self.default_size()) self.accel_group = Gtk.AccelGroup() self.win.add_accel_group(self.accel_group) self.but_cancel = self.win.add_button( set_underline(_("Cancel")), Gtk.ResponseType.CANCEL) self.but_cancel.set_image(common.IconFactory.get_image( 'tryton-cancel', Gtk.IconSize.BUTTON)) self.but_cancel.set_always_show_image(True) self.but_find = self.win.add_button( set_underline(_("Search")), Gtk.ResponseType.APPLY) self.but_find.set_image(common.IconFactory.get_image( 'tryton-search', Gtk.IconSize.BUTTON)) self.but_find.set_always_show_image(True) if new and common.MODELACCESS[model]['create']: self.but_new = self.win.add_button( set_underline(_("New")), Gtk.ResponseType.ACCEPT) self.but_new.set_image(common.IconFactory.get_image( 'tryton-create', Gtk.IconSize.BUTTON)) self.but_new.set_always_show_image(True) self.but_new.set_accel_path('<tryton>/Form/New', self.accel_group) self.but_ok = self.win.add_button( set_underline(_("OK")), Gtk.ResponseType.OK) self.but_ok.set_image(common.IconFactory.get_image( 'tryton-ok', Gtk.IconSize.BUTTON)) self.but_ok.set_always_show_image(True) self.but_ok.add_accelerator( 'clicked', self.accel_group, Gdk.KEY_Return, Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE) hbox = Gtk.HBox() hbox.show() self.win.vbox.pack_start(hbox, expand=False, fill=True, padding=0) self.win.vbox.pack_start( Gtk.HSeparator(), expand=False, fill=True, padding=0) self.screen = Screen(model, domain=domain, mode=['tree'], order=order, context=context, view_ids=view_ids, views_preload=views_preload, row_activate=self.sig_activate, readonly=True) self.view = self.screen.current_view # Prevent to set tree_state self.screen.tree_states_done.add(id(self.view)) sel = self.view.treeview.get_selection() self.win.set_title(_('Search %s') % self.title) if not sel_multi: sel.set_mode(Gtk.SelectionMode.SINGLE) else: sel.set_mode(Gtk.SelectionMode.MULTIPLE) self.win.vbox.pack_start( self.screen.widget, expand=True, fill=True, padding=0) self.screen.widget.show() self.model_name = model self.register() def sig_activate(self, *args): self.view.treeview.stop_emission_by_name('row_activated') self.win.response(Gtk.ResponseType.OK) return True def destroy(self): self.screen.destroy() self.win.destroy() NoModal.destroy(self) def show(self): self.win.show() def hide(self): self.win.hide() def response(self, win, response_id): res = None if response_id == Gtk.ResponseType.OK: res = [r.id for r in self.screen.selected_records] elif response_id == Gtk.ResponseType.APPLY: self.screen.search_filter(self.screen.screen_container.get_text()) return elif response_id == Gtk.ResponseType.ACCEPT: # Remove first tree view as mode if form only view_ids = self.view_ids[1:] screen = Screen(self.model_name, domain=self.domain, context=self.context, order=self.order, mode=['form'], view_ids=view_ids, views_preload=self.views_preload, exclude_field=self.exclude_field) def callback(result): if result: record = screen.current_record res = [(record.id, record.value.get('rec_name', ''))] self.callback(res) else: self.callback(None) self.destroy() WinForm( screen, callback, new=True, save_current=True, title=self.title) return if res: group = self.screen.group res = [(id_, group.get(id_).value.get('rec_name', '')) for id_ in res] self.callback(res) self.destroy()
class Many2Many(Widget): expand = True def __init__(self, view, attrs): super(Many2Many, self).__init__(view, attrs) self.widget = gtk.Frame() self.widget.set_shadow_type(gtk.SHADOW_NONE) self.widget.get_accessible().set_name(attrs.get('string', '')) vbox = gtk.VBox(homogeneous=False, spacing=5) self.widget.add(vbox) self._readonly = True self._position = 0 hbox = gtk.HBox(homogeneous=False, spacing=0) hbox.set_border_width(2) label = gtk.Label(attrs.get('string', '')) label.set_alignment(0.0, 0.5) hbox.pack_start(label, expand=True, fill=True) hbox.pack_start(gtk.VSeparator(), expand=False, fill=True) tooltips = common.Tooltips() self.wid_text = PlaceholderEntry() self.wid_text.set_placeholder_text(_('Search')) self.wid_text.set_property('width_chars', 13) self.wid_text.connect('focus-out-event', lambda *a: self._focus_out()) self.focus_out = True hbox.pack_start(self.wid_text, expand=True, fill=True) if int(self.attrs.get('completion', 1)): self.wid_completion = get_completion( create=self.attrs.get('create', True) and common.MODELACCESS[self.attrs['relation']]['create']) self.wid_completion.connect('match-selected', self._completion_match_selected) self.wid_completion.connect('action-activated', self._completion_action_activated) self.wid_text.set_completion(self.wid_completion) self.wid_text.connect('changed', self._update_completion) else: self.wid_completion = None self.but_add = gtk.Button() tooltips.set_tip(self.but_add, _('Add existing record')) self.but_add.connect('clicked', self._sig_add) img_add = gtk.Image() img_add.set_from_stock('tryton-list-add', gtk.ICON_SIZE_SMALL_TOOLBAR) img_add.set_alignment(0.5, 0.5) self.but_add.add(img_add) self.but_add.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_add, expand=False, fill=False) self.but_remove = gtk.Button() tooltips.set_tip(self.but_remove, _('Remove selected record <Del>')) self.but_remove.connect('clicked', self._sig_remove) img_remove = gtk.Image() img_remove.set_from_stock('tryton-list-remove', gtk.ICON_SIZE_SMALL_TOOLBAR) img_remove.set_alignment(0.5, 0.5) self.but_remove.add(img_remove) self.but_remove.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_remove, expand=False, fill=False) hbox.set_focus_chain([self.wid_text]) tooltips.enable() frame = gtk.Frame() frame.add(hbox) if attrs.get('expand_toolbar'): frame.set_shadow_type(gtk.SHADOW_NONE) else: frame.set_shadow_type(gtk.SHADOW_OUT) vbox.pack_start(frame, expand=False, fill=True) self.screen = Screen(attrs['relation'], view_ids=attrs.get('view_ids', '').split(','), mode=['tree'], views_preload=attrs.get('views', {}), row_activate=self._on_activate) self.screen.signal_connect(self, 'record-message', self._sig_label) vbox.pack_start(self.screen.widget, expand=True, fill=True) self.screen.widget.connect('key_press_event', self.on_keypress) self.wid_text.connect('key_press_event', self.on_keypress) def _color_widget(self): if hasattr(self.screen.current_view, 'treeview'): return self.screen.current_view.treeview return super(Many2Many, self)._color_widget() def on_keypress(self, widget, event): editable = self.wid_text.get_editable() activate_keys = [gtk.keysyms.Tab, gtk.keysyms.ISO_Left_Tab] remove_keys = [gtk.keysyms.Delete, gtk.keysyms.KP_Delete] if not self.wid_completion: activate_keys.append(gtk.keysyms.Return) if widget == self.screen.widget: if event.keyval == gtk.keysyms.F3 and editable: self._sig_add() return True elif event.keyval == gtk.keysyms.F2: self._sig_edit() return True elif event.keyval in remove_keys and editable: self._sig_remove() return True elif widget == self.wid_text: if event.keyval == gtk.keysyms.F3: self._sig_new() return True elif event.keyval == gtk.keysyms.F2: self._sig_add() return True elif event.keyval in activate_keys and self.wid_text.get_text(): self._sig_add() self.wid_text.grab_focus() return False def destroy(self): self.screen.destroy() def color_set(self, name): super(Many2Many, self).color_set(name) widget = self._color_widget() # if the style to apply is different from readonly then insensitive # cellrenderers should use the default insensitive color if name != 'readonly': widget.modify_text(gtk.STATE_INSENSITIVE, self.colors['text_color_insensitive']) def _sig_add(self, *args): if not self.focus_out: return domain = self.field.domain_get(self.record) add_remove = self.record.expr_eval(self.attrs.get('add_remove')) if add_remove: domain = [domain, add_remove] context = self.field.context_get(self.record) value = self.wid_text.get_text().decode('utf-8') self.focus_out = False def callback(result): self.focus_out = True if result: ids = [x[0] for x in result] self.screen.load(ids, modified=True) self.screen.display(res_id=ids[0]) self.screen.set_cursor() self.wid_text.set_text('') win = WinSearch(self.attrs['relation'], callback, sel_multi=True, context=context, domain=domain, view_ids=self.attrs.get('view_ids', '').split(','), views_preload=self.attrs.get('views', {}), new=self.attrs.get('create', True)) win.screen.search_filter(quote(value)) win.show() def _sig_remove(self, *args): self.screen.remove(remove=True) def _on_activate(self): self._sig_edit() def _sig_edit(self): if not self.screen.current_record: return # Create a new screen that is not linked to the parent otherwise on the # save of the record will trigger the save of the parent domain = self.field.domain_get(self.record) add_remove = self.record.expr_eval(self.attrs.get('add_remove')) if add_remove: domain = [domain, add_remove] context = self.field.context_get(self.record) screen = Screen(self.attrs['relation'], domain=domain, view_ids=self.attrs.get('view_ids', '').split(','), mode=['form'], views_preload=self.attrs.get('views', {}), readonly=self.attrs.get('readonly', False), context=context) screen.load([self.screen.current_record.id]) def callback(result): if result: screen.current_record.save() # Force a reload on next display self.screen.current_record.cancel() # Force a display to clear the CellCache self.screen.display() WinForm(screen, callback) def _sig_new(self): domain = self.field.domain_get(self.record) add_remove = self.record.expr_eval(self.attrs.get('add_remove')) if add_remove: domain = [domain, add_remove] context = self.field.context_get(self.record) screen = Screen(self.attrs['relation'], domain=domain, view_ids=self.attrs.get('view_ids', '').split(','), mode=['form'], views_preload=self.attrs.get('views', {}), context=context) def callback(result): self.focus_out = True if result: record = screen.current_record self.screen.load([record.id], modified=True) self.wid_text.set_text('') self.wid_text.grab_focus() self.focus_out = False WinForm(screen, callback, new=True, save_current=True) def _readonly_set(self, value): self._readonly = value self._set_button_sensitive() self.wid_text.set_sensitive(not value) def _set_button_sensitive(self): if self.record and self.field: field_size = self.record.expr_eval(self.attrs.get('size')) m2m_size = len(self.field.get_eval(self.record)) size_limit = (field_size is not None and m2m_size >= field_size >= 0) else: size_limit = False self.but_add.set_sensitive(bool( not self._readonly and not size_limit)) self.but_remove.set_sensitive(bool( not self._readonly and self._position)) def _sig_label(self, screen, signal_data): self._position = signal_data[0] self._set_button_sensitive() def display(self, record, field): super(Many2Many, self).display(record, field) if field is None: self.screen.new_group() self.screen.current_record = None self.screen.parent = None self.screen.display() return False new_group = field.get_client(record) if id(self.screen.group) != id(new_group): self.screen.group = new_group self.screen.display() return True def set_value(self, record, field): self.screen.current_view.set_value() return True def _completion_match_selected(self, completion, model, iter_): record_id, = model.get(iter_, 1) self.screen.load([record_id], modified=True) self.wid_text.set_text('') self.wid_text.grab_focus() completion_model = self.wid_completion.get_model() completion_model.clear() completion_model.search_text = self.wid_text.get_text() return True def _update_completion(self, widget): if self._readonly: return if not self.record: return model = self.attrs['relation'] update_completion(self.wid_text, self.record, self.field, model) def _completion_action_activated(self, completion, index): if index == 0: self._sig_add() self.wid_text.grab_focus() elif index == 1: self._sig_new()
class Many2Many(Widget): expand = True def __init__(self, view, attrs): super(Many2Many, self).__init__(view, attrs) self.widget = gtk.Frame() self.widget.set_shadow_type(gtk.SHADOW_NONE) self.widget.get_accessible().set_name(attrs.get('string', '')) vbox = gtk.VBox(homogeneous=False, spacing=5) self.widget.add(vbox) self._readonly = True self._required = False self._position = 0 hbox = gtk.HBox(homogeneous=False, spacing=0) hbox.set_border_width(2) self.title = gtk.Label(attrs.get('string', '')) self.title.set_alignment(0.0, 0.5) hbox.pack_start(self.title, expand=True, fill=True) hbox.pack_start(gtk.VSeparator(), expand=False, fill=True) tooltips = common.Tooltips() self.wid_text = PlaceholderEntry() self.wid_text.set_placeholder_text(_('Search')) self.wid_text.set_property('width_chars', 13) self.wid_text.connect('focus-out-event', lambda *a: self._focus_out()) self.focus_out = True hbox.pack_start(self.wid_text, expand=True, fill=True) if int(self.attrs.get('completion', 1)): self.wid_completion = get_completion( create=self.attrs.get('create', True) and common.MODELACCESS[self.attrs['relation']]['create']) self.wid_completion.connect('match-selected', self._completion_match_selected) self.wid_completion.connect('action-activated', self._completion_action_activated) self.wid_text.set_completion(self.wid_completion) self.wid_text.connect('changed', self._update_completion) else: self.wid_completion = None self.but_add = gtk.Button() tooltips.set_tip(self.but_add, _('Add existing record')) self.but_add.connect('clicked', self._sig_add) img_add = gtk.Image() img_add.set_from_stock('tryton-list-add', gtk.ICON_SIZE_SMALL_TOOLBAR) img_add.set_alignment(0.5, 0.5) self.but_add.add(img_add) self.but_add.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_add, expand=False, fill=False) self.but_remove = gtk.Button() tooltips.set_tip(self.but_remove, _('Remove selected record <Del>')) self.but_remove.connect('clicked', self._sig_remove) img_remove = gtk.Image() img_remove.set_from_stock('tryton-list-remove', gtk.ICON_SIZE_SMALL_TOOLBAR) img_remove.set_alignment(0.5, 0.5) self.but_remove.add(img_remove) self.but_remove.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_remove, expand=False, fill=False) hbox.set_focus_chain([self.wid_text]) tooltips.enable() frame = gtk.Frame() frame.add(hbox) frame.set_shadow_type(gtk.SHADOW_OUT) vbox.pack_start(frame, expand=False, fill=True) self.screen = Screen(attrs['relation'], view_ids=attrs.get('view_ids', '').split(','), mode=['tree'], views_preload=attrs.get('views', {}), row_activate=self._on_activate, limit=None) self.screen.signal_connect(self, 'record-message', self._sig_label) vbox.pack_start(self.screen.widget, expand=True, fill=True) self.screen.widget.connect('key_press_event', self.on_keypress) self.wid_text.connect('key_press_event', self.on_keypress) def on_keypress(self, widget, event): editable = self.wid_text.get_editable() activate_keys = [gtk.keysyms.Tab, gtk.keysyms.ISO_Left_Tab] remove_keys = [gtk.keysyms.Delete, gtk.keysyms.KP_Delete] if not self.wid_completion: activate_keys.append(gtk.keysyms.Return) if widget == self.screen.widget: if event.keyval == gtk.keysyms.F3 and editable: self._sig_add() return True elif event.keyval == gtk.keysyms.F2: self._sig_edit() return True elif event.keyval in remove_keys and editable: self._sig_remove() return True elif widget == self.wid_text: if event.keyval == gtk.keysyms.F3: self._sig_new() return True elif event.keyval == gtk.keysyms.F2: self._sig_add() return True elif event.keyval in activate_keys and self.wid_text.get_text(): self._sig_add() self.wid_text.grab_focus() return False def destroy(self): self.screen.destroy() def _sig_add(self, *args): if not self.focus_out: return domain = self.field.domain_get(self.record) add_remove = self.record.expr_eval(self.attrs.get('add_remove')) if add_remove: domain = [domain, add_remove] context = self.field.get_context(self.record) value = self.wid_text.get_text().decode('utf-8') self.focus_out = False def callback(result): self.focus_out = True if result: ids = [x[0] for x in result] self.screen.load(ids, modified=True) self.screen.display(res_id=ids[0]) self.screen.set_cursor() self.wid_text.set_text('') win = WinSearch(self.attrs['relation'], callback, sel_multi=True, context=context, domain=domain, view_ids=self.attrs.get('view_ids', '').split(','), views_preload=self.attrs.get('views', {}), new=self.attrs.get('create', True), title=self.attrs.get('string')) win.screen.search_filter(quote(value)) win.show() def _sig_remove(self, *args): self.screen.remove(remove=True) def _on_activate(self): self._sig_edit() def _get_screen_form(self): domain = self.field.domain_get(self.record) add_remove = self.record.expr_eval(self.attrs.get('add_remove')) if add_remove: domain = [domain, add_remove] context = self.field.get_context(self.record) # Remove the first tree view as mode is form only view_ids = self.attrs.get('view_ids', '').split(',')[1:] return Screen(self.attrs['relation'], domain=domain, view_ids=view_ids, mode=['form'], views_preload=self.attrs.get('views', {}), context=context) def _sig_edit(self): if not self.screen.current_record: return # Create a new screen that is not linked to the parent otherwise on the # save of the record will trigger the save of the parent screen = self._get_screen_form() screen.load([self.screen.current_record.id]) def callback(result): if result: screen.current_record.save() # Force a reload on next display self.screen.current_record.cancel() # Force a display to clear the CellCache self.screen.display() WinForm(screen, callback, title=self.attrs.get('string')) def _sig_new(self): screen = self._get_screen_form() def callback(result): self.focus_out = True if result: record = screen.current_record self.screen.load([record.id], modified=True) self.wid_text.set_text('') self.wid_text.grab_focus() self.focus_out = False WinForm(screen, callback, new=True, save_current=True, title=self.attrs.get('string'), rec_name=self.wid_text.get_text()) def _readonly_set(self, value): self._readonly = value self._set_button_sensitive() self.wid_text.set_sensitive(not value) self._set_label_state() def _required_set(self, value): self._required = value self._set_label_state() def _set_label_state(self): attrlist = common.get_label_attributes(self._readonly, self._required) self.title.set_attributes(attrlist) widget_class(self.title, 'readonly', self._readonly) widget_class(self.title, 'required', self._required) def _set_button_sensitive(self): if self.record and self.field: field_size = self.record.expr_eval(self.attrs.get('size')) m2m_size = len(self.field.get_eval(self.record)) size_limit = (field_size is not None and m2m_size >= field_size >= 0) else: size_limit = False self.but_add.set_sensitive(bool(not self._readonly and not size_limit)) self.but_remove.set_sensitive( bool(not self._readonly and self._position)) def _sig_label(self, screen, signal_data): self._position = signal_data[0] self._set_button_sensitive() def display(self, record, field): super(Many2Many, self).display(record, field) if field is None: self.screen.new_group() self.screen.current_record = None self.screen.parent = None self.screen.display() return False new_group = field.get_client(record) if id(self.screen.group) != id(new_group): self.screen.group = new_group self.screen.display() return True def set_value(self, record, field): self.screen.current_view.set_value() return True def _completion_match_selected(self, completion, model, iter_): record_id, = model.get(iter_, 1) self.screen.load([record_id], modified=True) self.wid_text.set_text('') self.wid_text.grab_focus() completion_model = self.wid_completion.get_model() completion_model.clear() completion_model.search_text = self.wid_text.get_text() return True def _update_completion(self, widget): if self._readonly: return if not self.record: return model = self.attrs['relation'] update_completion(self.wid_text, self.record, self.field, model) def _completion_action_activated(self, completion, index): if index == 0: self._sig_add() self.wid_text.grab_focus() elif index == 1: self._sig_new()
class Preference(object): "Preference window" def __init__(self, user): self.parent = common.get_toplevel_window() self.win = gtk.Dialog(_("Preferences"), self.parent, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT) self.win.set_position(gtk.WIN_POS_CENTER_ON_PARENT) self.win.set_has_separator(False) self.win.set_icon(TRYTON_ICON) self.accel_group = gtk.AccelGroup() self.win.add_accel_group(self.accel_group) self.but_cancel = self.win.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL) self.but_ok = self.win.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK) self.but_ok.add_accelerator( "clicked", self.accel_group, gtk.keysyms.Return, gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE ) self.win.set_default_response(gtk.RESPONSE_OK) try: view = RPCExecute("model", "res.user", "get_preferences_fields_view") except RPCException: self.win.destroy() self.win = None return title = gtk.Label(_("Edit User Preferences")) title.show() self.win.vbox.pack_start(title, expand=False, fill=True) self.screen = Screen("res.user", mode=[]) self.screen.add_view(view) self.screen.new(default=False) try: preferences = RPCExecute("model", "res.user", "get_preferences", False) except RPCException: self.win.destroy() self.win = None return self.screen.current_record.set(preferences) self.screen.current_record.validate(softvalidation=True) self.screen.screen_container.set(self.screen.current_view.widget) self.screen.display(set_cursor=True) self.screen.widget.show() self.win.vbox.pack_start(self.screen.widget) self.win.set_title(_("Preference")) width, height = self.parent.get_size() self.win.set_default_size(int(width * 0.9), int(height * 0.9)) self.win.show() def run(self): "Run the window" if not self.win: return False res = False while True: if self.win.run() == gtk.RESPONSE_OK: if self.screen.current_record.validate(): vals = copy.copy(self.screen.get(get_modifiedonly=True)) if "password" in vals: password = common.ask(_("Current Password:"******"model", "res.user", "set_preferences", vals, password) except RPCException: continue res = True break else: break self.parent.present() self.win.destroy() return res
class One2Many(Widget): expand = True def __init__(self, view, attrs): super(One2Many, self).__init__(view, attrs) self.widget = Gtk.Frame() self.widget.set_shadow_type(Gtk.ShadowType.NONE) self.widget.get_accessible().set_name(attrs.get('string', '')) vbox = Gtk.VBox(homogeneous=False, spacing=2) self.widget.add(vbox) self._readonly = True self._required = False self._position = 0 self._length = 0 self.title_box = hbox = Gtk.HBox(homogeneous=False, spacing=0) hbox.set_border_width(2) self.title = Gtk.Label(label=set_underline(attrs.get('string', '')), use_underline=True, halign=Gtk.Align.START) hbox.pack_start(self.title, expand=True, fill=True, padding=0) hbox.pack_start(Gtk.VSeparator(), expand=False, fill=True, padding=0) tooltips = common.Tooltips() but_switch = Gtk.Button(can_focus=False) tooltips.set_tip(but_switch, _('Switch')) but_switch.connect('clicked', self.switch_view) but_switch.add( common.IconFactory.get_image('tryton-switch', Gtk.IconSize.SMALL_TOOLBAR)) but_switch.set_relief(Gtk.ReliefStyle.NONE) hbox.pack_start(but_switch, expand=False, fill=False, padding=0) self.but_pre = Gtk.Button(can_focus=False) tooltips.set_tip(self.but_pre, _('Previous')) self.but_pre.connect('clicked', self._sig_previous) self.but_pre.add( common.IconFactory.get_image('tryton-back', Gtk.IconSize.SMALL_TOOLBAR)) self.but_pre.set_relief(Gtk.ReliefStyle.NONE) hbox.pack_start(self.but_pre, expand=False, fill=False, padding=0) self.label = Gtk.Label(label='(0,0)') hbox.pack_start(self.label, expand=False, fill=False, padding=0) self.but_next = Gtk.Button(can_focus=False) tooltips.set_tip(self.but_next, _('Next')) self.but_next.connect('clicked', self._sig_next) self.but_next.add( common.IconFactory.get_image('tryton-forward', Gtk.IconSize.SMALL_TOOLBAR)) self.but_next.set_relief(Gtk.ReliefStyle.NONE) hbox.pack_start(self.but_next, expand=False, fill=False, padding=0) hbox.pack_start(Gtk.VSeparator(), expand=False, fill=True, padding=0) self.focus_out = True self.wid_completion = None if attrs.get('add_remove'): self.wid_text = Gtk.Entry() self.wid_text.set_placeholder_text(_('Search')) self.wid_text.set_property('width_chars', 13) self.wid_text.connect('focus-out-event', self._focus_out) hbox.pack_start(self.wid_text, expand=True, fill=True, padding=0) if int(self.attrs.get('completion', 1)): self.wid_completion = get_completion(search=self.read_access, create=self.create_access) self.wid_completion.connect('match-selected', self._completion_match_selected) self.wid_completion.connect('action-activated', self._completion_action_activated) self.wid_text.set_completion(self.wid_completion) self.wid_text.connect('changed', self._update_completion) self.but_add = Gtk.Button(can_focus=False) tooltips.set_tip(self.but_add, _('Add existing record')) self.but_add.connect('clicked', self._sig_add) self.but_add.add( common.IconFactory.get_image('tryton-add', Gtk.IconSize.SMALL_TOOLBAR)) self.but_add.set_relief(Gtk.ReliefStyle.NONE) hbox.pack_start(self.but_add, expand=False, fill=False, padding=0) self.but_remove = Gtk.Button(can_focus=False) tooltips.set_tip(self.but_remove, _('Remove selected record')) self.but_remove.connect('clicked', self._sig_remove, True) self.but_remove.add( common.IconFactory.get_image('tryton-remove', Gtk.IconSize.SMALL_TOOLBAR)) self.but_remove.set_relief(Gtk.ReliefStyle.NONE) hbox.pack_start(self.but_remove, expand=False, fill=False, padding=0) hbox.pack_start(Gtk.VSeparator(), expand=False, fill=True, padding=0) self.but_new = Gtk.Button(can_focus=False) tooltips.set_tip(self.but_new, _('Create a new record')) self.but_new.connect('clicked', self._sig_new) self.but_new.add( common.IconFactory.get_image('tryton-create', Gtk.IconSize.SMALL_TOOLBAR)) self.but_new.set_relief(Gtk.ReliefStyle.NONE) hbox.pack_start(self.but_new, expand=False, fill=False, padding=0) self.but_open = Gtk.Button(can_focus=False) tooltips.set_tip(self.but_open, _('Edit selected record')) self.but_open.connect('clicked', self._sig_edit) self.but_open.add( common.IconFactory.get_image('tryton-open', Gtk.IconSize.SMALL_TOOLBAR)) self.but_open.set_relief(Gtk.ReliefStyle.NONE) hbox.pack_start(self.but_open, expand=False, fill=False, padding=0) self.but_del = Gtk.Button(can_focus=False) tooltips.set_tip(self.but_del, _('Delete selected record')) self.but_del.connect('clicked', self._sig_remove, False) self.but_del.add( common.IconFactory.get_image('tryton-delete', Gtk.IconSize.SMALL_TOOLBAR)) self.but_del.set_relief(Gtk.ReliefStyle.NONE) hbox.pack_start(self.but_del, expand=False, fill=False, padding=0) self.but_undel = Gtk.Button(can_focus=False) tooltips.set_tip(self.but_undel, _('Undelete selected record <Ins>')) self.but_undel.connect('clicked', self._sig_undelete) self.but_undel.add( common.IconFactory.get_image('tryton-undo', Gtk.IconSize.SMALL_TOOLBAR)) self.but_undel.set_relief(Gtk.ReliefStyle.NONE) hbox.pack_start(self.but_undel, expand=False, fill=False, padding=0) tooltips.enable() frame = Gtk.Frame() frame.add(hbox) frame.set_shadow_type(Gtk.ShadowType.OUT) vbox.pack_start(frame, expand=False, fill=True, padding=0) model = attrs['relation'] breadcrumb = list(self.view.screen.breadcrumb) breadcrumb.append(attrs.get('string') or common.MODELNAME.get(model)) self.screen = Screen(model, mode=attrs.get('mode', 'tree,form').split(','), view_ids=attrs.get('view_ids', '').split(','), views_preload=attrs.get('views', {}), order=attrs.get('order'), row_activate=self._on_activate, exclude_field=attrs.get('relation_field', None), limit=None, breadcrumb=breadcrumb) self.screen.pre_validate = bool(int(attrs.get('pre_validate', 0))) self.screen.windows.append(self) vbox.pack_start(self.screen.widget, expand=True, fill=True, padding=0) self.title.set_mnemonic_widget( self.screen.current_view.mnemonic_widget) self.screen.widget.connect('key_press_event', self.on_keypress) if self.attrs.get('add_remove'): self.wid_text.connect('key_press_event', self.on_keypress) but_switch.props.sensitive = self.screen.number_of_views > 1 def get_access(self, type_): model = self.attrs['relation'] if model: return common.MODELACCESS[model][type_] else: return True @property def read_access(self): return self.get_access('read') @property def create_access(self): return int(self.attrs.get('create', 1)) and self.get_access('create') @property def write_access(self): return self.get_access('write') @property def delete_access(self): return int(self.attrs.get('delete', 1)) and self.get_access('delete') def on_keypress(self, widget, event): if ((event.keyval == Gdk.KEY_F3) and self.but_new.get_property('sensitive')): self._sig_new(widget) return True if event.keyval == Gdk.KEY_F2: if widget == self.screen.widget: self._sig_edit(widget) return True elif widget == self.wid_text: self._sig_add(widget) return True if event.keyval == Gdk.KEY_F4: self.switch_view(widget) if (event.keyval in [Gdk.KEY_Delete, Gdk.KEY_KP_Delete] and widget == self.screen.widget): remove = not (event.state & Gdk.ModifierType.CONTROL_MASK) if remove and self.attrs.get('add_remove'): but = self.but_remove else: remove = False but = self.but_del if but.get_property('sensitive'): self._sig_remove(widget, remove) return True if event.keyval == Gdk.KEY_Insert and widget == self.screen.widget: self._sig_undelete(widget) return True if self.attrs.get('add_remove'): editable = self.wid_text.get_editable() activate_keys = [Gdk.KEY_Tab, Gdk.KEY_ISO_Left_Tab] if not self.wid_completion: activate_keys.append(Gdk.KEY_Return) if (widget == self.wid_text and event.keyval in activate_keys and editable and self.wid_text.get_text()): self._sig_add() self.wid_text.grab_focus() return False def destroy(self): if self.attrs.get('add_remove'): self.wid_text.disconnect_by_func(self._focus_out) self.screen.destroy() def _on_activate(self): self._sig_edit() def switch_view(self, widget): self.screen.switch_view() mnemonic_widget = self.screen.current_view.mnemonic_widget string = self.attrs.get('string', '') if mnemonic_widget: string = set_underline(string) self.title.set_mnemonic_widget(mnemonic_widget) self.title.set_label(string) @property def modified(self): return self.screen.current_view.modified def _readonly_set(self, value): self._readonly = value self._set_button_sensitive() self._set_label_state() def _required_set(self, value): self._required = value self._set_label_state() def _set_label_state(self): common.apply_label_attributes(self.title, self._readonly, self._required) def _set_button_sensitive(self): if self.record and self.field: field_size = self.record.expr_eval(self.attrs.get('size')) o2m_size = len(self.field.get_eval(self.record)) size_limit = (field_size is not None and o2m_size >= field_size >= 0) else: o2m_size = None size_limit = False first = last = False if isinstance(self._position, int): first = self._position <= 1 last = self._position >= self._length deletable = self.screen.deletable self.but_new.set_sensitive( bool(not self._readonly and self.create_access and not size_limit)) self.but_del.set_sensitive( bool(not self._readonly and self.delete_access and deletable and self._position)) self.but_undel.set_sensitive( bool(not self._readonly and not size_limit and self._position)) self.but_open.set_sensitive(bool(self._position and self.read_access)) self.but_next.set_sensitive(bool(self._position and not last)) self.but_pre.set_sensitive(bool(self._position and not first)) if self.attrs.get('add_remove'): self.but_add.set_sensitive( bool(not self._readonly and not size_limit and self.write_access and self.read_access)) self.but_remove.set_sensitive( bool(not self._readonly and self._position and self.write_access and self.read_access)) self.wid_text.set_sensitive(self.but_add.get_sensitive()) self.wid_text.set_editable(self.but_add.get_sensitive()) def _validate(self): self.view.set_value() record = self.screen.current_record if record: fields = self.screen.current_view.get_fields() if not record.validate(fields): self.screen.display(set_cursor=True) return False if self.screen.pre_validate and not record.pre_validate(): return False return True def _sequence(self): for view in self.screen.views: if view.view_type == 'tree': sequence = view.attributes.get('sequence') if sequence: return sequence def _sig_new(self, *args): if not self.create_access: return if not self._validate(): return if self.attrs.get('product'): self._new_product() else: self._new_single() def _new_single(self): sequence = self._sequence() def update_sequence(): if sequence: self.screen.group.set_sequence( field=sequence, position=self.screen.new_position) if self.screen.current_view.creatable: self.screen.new() self.screen.current_view.widget.set_sensitive(True) update_sequence() else: field_size = self.record.expr_eval(self.attrs.get('size')) or -1 field_size -= len(self.field.get_eval(self.record)) + 1 WinForm(self.screen, lambda a: update_sequence(), new=True, many=field_size) def _new_product(self): fields = self.attrs['product'].split(',') product = {} first = self.screen.new(default=False) default = first.default_get() first.set_default(default) def search_set(*args): if not fields: return make_product() field = self.screen.group.fields[fields.pop()] relation = field.attrs.get('relation') if not relation: search_set() domain = field.domain_get(first) context = field.get_search_context(first) order = field.get_search_order(first) def callback(result): if result: product[field.name] = result win_search = WinSearch(relation, callback, sel_multi=True, context=context, domain=domain, order=order, title=self.attrs.get('string')) win_search.win.connect('destroy', search_set) win_search.screen.search_filter() win_search.show() def make_product(): self.screen.group.remove(first, remove=True) if not product: return fields = list(product.keys()) for values in itertools.product(*list(product.values())): record = self.screen.new(default=False) default_value = default.copy() for field, value in zip(fields, values): id_, rec_name = value default_value[field] = id_ default_value[field + '.rec_name'] = rec_name record.set_default(default_value) sequence = self._sequence() if sequence: self.screen.group.set_sequence( field=sequence, position=self.screen.new_position) search_set() def _sig_edit(self, widget=None): if not common.MODELACCESS[self.screen.model_name]['read']: return if not self._validate(): return record = self.screen.current_record if record: WinForm(self.screen, lambda a: None) def _sig_next(self, widget): if not self._validate(): return self.screen.display_next() def _sig_previous(self, widget): if not self._validate(): return self.screen.display_prev() def _sig_remove(self, widget, remove=False): writable = not self.screen.readonly deletable = self.screen.deletable if remove: if not self.write_access or not writable or not self.read_access: return else: if not self.delete_access or not deletable: return self.screen.remove(remove=remove) def _sig_undelete(self, button): self.screen.unremove() def _sig_add(self, *args): if not self.focus_out: return if not self.write_access or not self.read_access: return self.view.set_value() domain = self.field.domain_get(self.record) context = self.field.get_search_context(self.record) domain = [domain, self.record.expr_eval(self.attrs.get('add_remove'))] removed_ids = self.field.get_removed_ids(self.record) domain = ['OR', domain, ('id', 'in', removed_ids)] text = self.wid_text.get_text() self.focus_out = False sequence = self._sequence() def callback(result): self.focus_out = True if result: ids = [x[0] for x in result] self.screen.load(ids, modified=True) self.screen.display(res_id=ids[0]) if sequence: self.screen.group.set_sequence( field=sequence, position=self.screen.new_position) self.screen.set_cursor() self.wid_text.set_text('') order = self.field.get_search_order(self.record) win = WinSearch(self.attrs['relation'], callback, sel_multi=True, context=context, domain=domain, order=order, view_ids=self.attrs.get('view_ids', '').split(','), views_preload=self.attrs.get('views', {}), new=self.but_new.get_property('sensitive'), title=self.attrs.get('string'), exclude_field=self.attrs.get('relation_field')) win.screen.search_filter(quote(text)) win.show() def record_message(self, position, size, *args): self._position = position self._length = size if self._position: name = str(self._position) else: name = '_' line = '(%s/%s)' % (name, self._length) self.label.set_text(line) self._set_button_sensitive() def display(self): super(One2Many, self).display() self._set_button_sensitive() if not self.field: self.screen.new_group() self.screen.current_record = None self.screen.parent = None self.screen.display() return False new_group = self.field.get_client(self.record) if id(self.screen.group) != id(new_group): self.screen.group = new_group if (self.screen.current_view.view_type == 'tree') \ and self.screen.current_view.editable: self.screen.current_record = None domain = [] size_limit = None if self.record: domain = self.field.domain_get(self.record) size_limit = self.record.expr_eval(self.attrs.get('size')) if self._readonly: if size_limit is None: size_limit = len(self.screen.group) else: size_limit = min(size_limit, len(self.screen.group)) if self.screen.domain != domain: self.screen.domain = domain self.screen.size_limit = size_limit self.screen.display() return True def set_value(self): self.screen.current_view.set_value() if self.screen.modified(): # TODO check if required self.view.screen.record_modified(display=False) return True def _completion_match_selected(self, completion, model, iter_): record_id, = model.get(iter_, 1) self.screen.load([record_id], modified=True) self.wid_text.set_text('') self.wid_text.grab_focus() completion_model = self.wid_completion.get_model() completion_model.clear() completion_model.search_text = self.wid_text.get_text() return True def _update_completion(self, widget): if self._readonly: return if not self.record: return model = self.attrs['relation'] domain = self.field.domain_get(self.record) domain = [domain, self.record.expr_eval(self.attrs.get('add_remove'))] removed_ids = self.field.get_removed_ids(self.record) domain = ['OR', domain, ('id', 'in', removed_ids)] update_completion(self.wid_text, self.record, self.field, model, domain=domain) def _completion_action_activated(self, completion, index): if index == 0: self._sig_add() self.wid_text.grab_focus() elif index == 1: self._sig_new()
def __init__(self, view, attrs): super(Many2Many, self).__init__(view, attrs) self.widget = gtk.Frame() self.widget.set_shadow_type(gtk.SHADOW_NONE) self.widget.get_accessible().set_name(attrs.get('string', '')) vbox = gtk.VBox(homogeneous=False, spacing=5) self.widget.add(vbox) self._readonly = True self._position = 0 hbox = gtk.HBox(homogeneous=False, spacing=0) hbox.set_border_width(2) label = gtk.Label(attrs.get('string', '')) label.set_alignment(0.0, 0.5) hbox.pack_start(label, expand=True, fill=True) hbox.pack_start(gtk.VSeparator(), expand=False, fill=True) tooltips = common.Tooltips() self.wid_text = PlaceholderEntry() self.wid_text.set_placeholder_text(_('Search')) self.wid_text.set_property('width_chars', 13) self.wid_text.connect('focus-out-event', lambda *a: self._focus_out()) self.focus_out = True hbox.pack_start(self.wid_text, expand=True, fill=True) if int(self.attrs.get('completion', 1)): self.wid_completion = get_completion( create=self.attrs.get('create', True) and common.MODELACCESS[self.attrs['relation']]['create']) self.wid_completion.connect('match-selected', self._completion_match_selected) self.wid_completion.connect('action-activated', self._completion_action_activated) self.wid_text.set_completion(self.wid_completion) self.wid_text.connect('changed', self._update_completion) else: self.wid_completion = None self.but_add = gtk.Button() tooltips.set_tip(self.but_add, _('Add existing record')) self.but_add.connect('clicked', self._sig_add) img_add = gtk.Image() img_add.set_from_stock('tryton-list-add', gtk.ICON_SIZE_SMALL_TOOLBAR) img_add.set_alignment(0.5, 0.5) self.but_add.add(img_add) self.but_add.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_add, expand=False, fill=False) self.but_remove = gtk.Button() tooltips.set_tip(self.but_remove, _('Remove selected record <Del>')) self.but_remove.connect('clicked', self._sig_remove) img_remove = gtk.Image() img_remove.set_from_stock('tryton-list-remove', gtk.ICON_SIZE_SMALL_TOOLBAR) img_remove.set_alignment(0.5, 0.5) self.but_remove.add(img_remove) self.but_remove.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_remove, expand=False, fill=False) hbox.set_focus_chain([self.wid_text]) tooltips.enable() frame = gtk.Frame() frame.add(hbox) if attrs.get('expand_toolbar'): frame.set_shadow_type(gtk.SHADOW_NONE) else: frame.set_shadow_type(gtk.SHADOW_OUT) vbox.pack_start(frame, expand=False, fill=True) self.screen = Screen(attrs['relation'], view_ids=attrs.get('view_ids', '').split(','), mode=['tree'], views_preload=attrs.get('views', {}), row_activate=self._on_activate) self.screen.signal_connect(self, 'record-message', self._sig_label) vbox.pack_start(self.screen.widget, expand=True, fill=True) self.screen.widget.connect('key_press_event', self.on_keypress) self.wid_text.connect('key_press_event', self.on_keypress)
class Action(SignalEvent): def __init__(self, attrs=None, context=None): super(Action, self).__init__() self.name = attrs['name'] self.context = context or {} try: self.action = RPCExecute('model', 'ir.action.act_window', 'get', self.name) except RPCException: raise view_ids = None self.action['view_mode'] = None if self.action.get('views', []): view_ids = [x[0] for x in self.action['views']] self.action['view_mode'] = [x[1] for x in self.action['views']] elif self.action.get('view_id', False): view_ids = [self.action['view_id'][0]] if 'view_mode' in attrs: self.action['view_mode'] = attrs['view_mode'] self.action.setdefault('pyson_domain', '[]') self.context.update(rpc.CONTEXT) self.context['_user'] = rpc._USER self.context.update( PYSONDecoder(self.context).decode( self.action.get('pyson_context', '{}'))) eval_ctx = self.context.copy() self.context.update( PYSONDecoder(eval_ctx).decode( self.action.get('pyson_context', '{}'))) self.domain = [] self.update_domain([]) search_context = self.context.copy() search_context['context'] = self.context search_context['_user'] = rpc._USER search_value = PYSONDecoder(search_context).decode( self.action['pyson_search_value'] or '{}') self.widget = gtk.Frame() self.widget.set_border_width(0) vbox = gtk.VBox(homogeneous=False, spacing=3) self.widget.add(vbox) self.title = gtk.Label() self.widget.set_label_widget(self.title) self.widget.set_label_align(0.0, 0.5) self.widget.show_all() self.screen = Screen(self.action['res_model'], mode=self.action['view_mode'], context=self.context, view_ids=view_ids, domain=self.domain, search_value=search_value, row_activate=self.row_activate) vbox.pack_start(self.screen.widget, expand=True, fill=True) self.screen.signal_connect(self, 'record-message', self._active_changed) if attrs.get('string'): self.title.set_text(attrs['string']) else: self.title.set_text(self.action['name']) self.widget.set_size_request(int(attrs.get('width', -1)), int(attrs.get('height', -1))) self.screen.search_filter() def row_activate(self): if not self.screen.current_record: return if (self.screen.current_view.view_type == 'tree' and int( self.screen.current_view.attributes.get('keyword_open', 0))): GenericAction.exec_keyword('tree_open', { 'model': self.screen.model_name, 'id': (self.screen.current_record.id if self.screen.current_record else None), 'ids': [r.id for r in self.screen.selected_records], }, context=self.screen.context.copy(), warning=False) else: def callback(result): if result: self.screen.current_record.save() else: self.screen.current_record.cancel() WinForm(self.screen, callback) def set_value(self, mode, model_field): self.screen.current_view.set_value() return True def display(self): self.screen.search_filter(self.screen.screen_container.get_text()) def _active_changed(self, *args): self.signal('active-changed') def _get_active(self): if self.screen and self.screen.current_record: return common.EvalEnvironment(self.screen.current_record) active = property(_get_active) def update_domain(self, actions): domain_ctx = self.context.copy() domain_ctx['context'] = domain_ctx domain_ctx['_user'] = rpc._USER for action in actions: if action.active: domain_ctx[action.name] = action.active new_domain = PYSONDecoder(domain_ctx).decode( self.action['pyson_domain']) if self.domain == new_domain: return del self.domain[:] self.domain.extend(new_domain) if hasattr(self, 'screen'): # Catch early update # Using idle_add to prevent corruption of the event who triggered # the update. def display(): if self.screen.widget.props.window: self.display() gtk.idle_add(display)
class Many2Many(WidgetInterface): def __init__(self, field_name, model_name, attrs=None): super(Many2Many, self).__init__(field_name, model_name, attrs=attrs) self.widget = gtk.VBox(homogeneous=False, spacing=5) self._readonly = True self._position = 0 hbox = gtk.HBox(homogeneous=False, spacing=0) hbox.set_border_width(2) label = gtk.Label(attrs.get('string', '')) label.set_alignment(0.0, 0.5) hbox.pack_start(label, expand=True, fill=True) hbox.pack_start(gtk.VSeparator(), expand=False, fill=True) tooltips = common.Tooltips() self.wid_text = PlaceholderEntry() self.wid_text.set_placeholder_text(_('Search')) self.wid_text.set_property('width_chars', 13) self.wid_text.connect('focus-out-event', lambda *a: self._focus_out()) self.focus_out = True hbox.pack_start(self.wid_text, expand=True, fill=True) if int(self.attrs.get('completion', 1)): self.wid_completion = get_completion() self.wid_completion.connect('match-selected', self._completion_match_selected) self.wid_completion.connect('action-activated', self._completion_action_activated) self.wid_text.set_completion(self.wid_completion) self.wid_text.connect('changed', self._update_completion) else: self.wid_completion = None self.but_add = gtk.Button() tooltips.set_tip(self.but_add, _('Add existing record')) self.but_add.connect('clicked', self._sig_add) img_add = gtk.Image() img_add.set_from_stock('tryton-list-add', gtk.ICON_SIZE_SMALL_TOOLBAR) img_add.set_alignment(0.5, 0.5) self.but_add.add(img_add) self.but_add.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_add, expand=False, fill=False) self.but_remove = gtk.Button() tooltips.set_tip(self.but_remove, _('Remove selected record <Del>')) self.but_remove.connect('clicked', self._sig_remove) img_remove = gtk.Image() img_remove.set_from_stock('tryton-list-remove', gtk.ICON_SIZE_SMALL_TOOLBAR) img_remove.set_alignment(0.5, 0.5) self.but_remove.add(img_remove) self.but_remove.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_remove, expand=False, fill=False) hbox.set_focus_chain([self.wid_text]) tooltips.enable() frame = gtk.Frame() frame.add(hbox) frame.set_shadow_type(gtk.SHADOW_OUT) self.widget.pack_start(frame, expand=False, fill=True) self.screen = Screen(attrs['relation'], view_ids=attrs.get('view_ids', '').split(','), mode=['tree'], views_preload=attrs.get('views', {}), row_activate=self._on_activate) self.screen.signal_connect(self, 'record-message', self._sig_label) self.widget.pack_start(self.screen.widget, expand=True, fill=True) self.screen.widget.connect('key_press_event', self.on_keypress) self.wid_text.connect('key_press_event', self.on_keypress) def _color_widget(self): if hasattr(self.screen.current_view, 'widget_tree'): return self.screen.current_view.widget_tree return super(Many2Many, self)._color_widget() def grab_focus(self): return self.wid_text.grab_focus() def on_keypress(self, widget, event): editable = self.wid_text.get_editable() activate_keys = [gtk.keysyms.Tab, gtk.keysyms.ISO_Left_Tab] if not self.wid_completion: activate_keys.append(gtk.keysyms.Return) if event.keyval == gtk.keysyms.F3: self._sig_add() return True if event.keyval == gtk.keysyms.F2 \ and widget == self.screen.widget: self._sig_edit() return True if event.keyval in (gtk.keysyms.Delete, gtk.keysyms.KP_Delete) \ and widget == self.screen.widget: self._sig_remove() return True if (widget == self.wid_text and event.keyval in activate_keys and editable and self.wid_text.get_text()): self._sig_add() self.wid_text.grab_focus() return False def destroy(self): self.screen.destroy() self.widget.destroy() del self.widget def color_set(self, name): super(Many2Many, self).color_set(name) widget = self._color_widget() # if the style to apply is different from readonly then insensitive # cellrenderers should use the default insensitive color if name != 'readonly': widget.modify_text(gtk.STATE_INSENSITIVE, self.colors['text_color_insensitive']) def _sig_add(self, *args, **kwargs): if not self.focus_out: return domain = self.field.domain_get(self.record) context = self.field.context_get(self.record) value = self.wid_text.get_text() self.focus_out = False try: if value: dom = [('rec_name', 'ilike', '%' + value + '%'), domain] else: dom = domain ids = RPCExecute('model', self.attrs['relation'], 'search', dom, 0, CONFIG['client.limit'], None, context=context) except RPCException: self.focus_out = True return False def callback(result): self.focus_out = True if result: ids = [x[0] for x in result] self.screen.load(ids, modified=True) self.screen.display(res_id=ids[0]) self.screen.set_cursor() self.wid_text.set_text('') if len(ids) != 1 or not value or kwargs.get('win_search', False): WinSearch(self.attrs['relation'], callback, sel_multi=True, ids=ids, context=context, domain=domain, view_ids=self.attrs.get('view_ids', '').split(','), views_preload=self.attrs.get('views', {}), new=self.attrs.get('create', True)) else: callback([(i, None) for i in ids]) def _sig_remove(self, *args): self.screen.remove(remove=True) def _on_activate(self): self._sig_edit() def _sig_edit(self): if self.screen.current_record: def callback(result): if result: self.screen.current_record.save() else: self.screen.current_record.cancel() WinForm(self.screen, callback) def _readonly_set(self, value): self._readonly = value self._set_button_sensitive() def _set_button_sensitive(self): if self.record and self.field: field_size = self.record.expr_eval(self.attrs.get('size')) m2m_size = len(self.field.get_eval(self.record)) size_limit = (field_size is not None and m2m_size >= field_size >= 0) else: size_limit = False self.wid_text.set_sensitive(not self._readonly) self.but_add.set_sensitive(bool( not self._readonly and not size_limit)) self.but_remove.set_sensitive(bool( not self._readonly and self._position)) def _sig_label(self, screen, signal_data): self._position = signal_data[0] self._set_button_sensitive() def display(self, record, field): super(Many2Many, self).display(record, field) if field is None: self.screen.new_group() self.screen.current_record = None self.screen.parent = True self.screen.display() return False new_group = field.get_client(record) if id(self.screen.group) != id(new_group): self.screen.group = new_group self.screen.display() return True def set_value(self, record, field): self.screen.save_tree_state() self.screen.current_view.set_value() return True def _completion_match_selected(self, completion, model, iter_): record_id, = model.get(iter_, 1) self.screen.load([record_id], modified=True) self.wid_text.set_text('') self.wid_text.grab_focus() completion_model = self.wid_completion.get_model() completion_model.clear() completion_model.search_text = self.wid_text.get_text() return True def _update_completion(self, widget): if self._readonly: return if not self.record: return model = self.attrs['relation'] update_completion(self.wid_text, self.record, self.field, model) def _completion_action_activated(self, completion, index): if index == 0: self._sig_add(win_search=True) self.wid_text.grab_focus() elif index == 1: model = self.attrs['relation'] domain = self.field.domain_get(self.record) context = self.field.context_get(self.record) screen = Screen(model, domain, context=context, mode=['form']) def callback(result): self.focus_out = True if result: record = screen.current_record self.screen.load([record.id], modified=True) self.wid_text.set_text('') self.wid_text.grab_focus() self.focus_out = False WinForm(screen, callback, new=True, save_current=True)
class Form(SignalEvent, TabContent): "Form" def __init__(self, model, res_id=None, name='', **attributes): super(Form, self).__init__(**attributes) self.model = model self.res_id = res_id self.mode = attributes.get('mode') self.view_ids = attributes.get('view_ids') self.dialogs = [] self.screen = Screen(self.model, **attributes) self.screen.widget.show() self.name = name self.create_tabcontent() self.set_buttons_sensitive() self.screen.signal_connect(self, 'record-message', self._record_message) self.screen.signal_connect( self, 'record-modified', lambda *a: GLib.idle_add(self._record_modified, *a)) self.screen.signal_connect(self, 'record-saved', self._record_saved) self.screen.signal_connect( self, 'resources', lambda screen, resources: self.update_resources(resources)) if res_id not in (None, False): if isinstance(res_id, int): res_id = [res_id] self.screen.load(res_id) else: if self.screen.current_view.view_type == 'form': self.sig_new(None, autosave=False) if self.screen.current_view.view_type \ in ('tree', 'graph', 'calendar'): self.screen.search_filter() self.update_revision() def get_toolbars(self): try: return RPCExecute('model', self.model, 'view_toolbar_get', context=self.screen.context) except RPCException: return {} def widget_get(self): return self.screen.widget def compare(self, model, attributes): if not attributes: return False return (self.model == model and self.res_id == attributes.get('res_id') and self.attributes.get('domain') == attributes.get('domain') and self.attributes.get('view_ids') == attributes.get('view_ids') and (attributes.get('view_ids') or (self.attributes.get('mode') or ['tree', 'form']) == (attributes.get('mode') or ['tree', 'form'])) and self.screen.local_context == attributes.get('context') and self.attributes.get('search_value') == (attributes.get('search_value'))) def __hash__(self): return id(self) def destroy(self): super(Form, self).destroy() self.screen.signal_unconnect(self) self.screen.destroy() def sig_attach(self, widget=None): def window(widget): return Attachment(record, lambda: self.refresh_resources(reload=True)) def add_file(widget): filenames = common.file_selection(_("Select"), multi=True) if filenames: attachment = window(widget) for filename in filenames: attachment.add_file(filename) def activate(widget, callback): callback() button = self.buttons['attach'] if widget != button: if button.props.sensitive: button.props.active = True return record = self.screen.current_record menu = button._menu = Gtk.Menu() for name, callback in Attachment.get_attachments(record): item = Gtk.MenuItem(label=name) item.connect('activate', activate, callback) menu.add(item) menu.add(Gtk.SeparatorMenuItem()) add_item = Gtk.MenuItem(label=_("Add...")) add_item.connect('activate', add_file) menu.add(add_item) manage_item = Gtk.MenuItem(label=_("Manage...")) manage_item.connect('activate', window) menu.add(manage_item) menu.show_all() menu.connect('deactivate', self._popup_menu_hide, button) self.action_popup(button) def sig_note(self, widget=None): record = self.screen.current_record if not record or record.id < 0: return Note(record, lambda: self.refresh_resources(reload=True)) def refresh_resources(self, reload=False): record = self.screen.current_record self.update_resources( record.get_resources(reload=reload) if record else None) def update_resources(self, resources): if not resources: resources = {} record = self.screen.current_record sensitive = record.id >= 0 if record else False def update(name, label, icon, badge): button = self.buttons[name] button.set_label(label) image = common.IconFactory.get_image(icon, Gtk.IconSize.LARGE_TOOLBAR, badge=badge) image.show() button.set_icon_widget(image) button.props.sensitive = sensitive attachment_count = resources.get('attachment_count', 0) badge = 1 if attachment_count else None label = _("Attachment (%s)") % attachment_count update('attach', label, 'tryton-attach', badge) note_count = resources.get('note_count', 0) note_unread = resources.get('note_unread', 0) if note_unread: badge = 2 elif note_count: badge = 1 else: badge = None label = _("Note (%d/%d)") % (note_unread, note_count) update('note', label, 'tryton-note', badge) def sig_switch(self, widget=None): if not self.modified_save(): return self.screen.switch_view() def sig_logs(self, widget=None): current_record = self.screen.current_record if not current_record or current_record.id < 0: self.message_info(_('You have to select one record.'), Gtk.MessageType.INFO) return False fields = [ ('id', _('ID:')), ('create_uid.rec_name', _('Created by:')), ('create_date', _('Created at:')), ('write_uid.rec_name', _('Edited by:')), ('write_date', _('Edited at:')), ] try: data = RPCExecute('model', self.model, 'read', [current_record.id], [x[0] for x in fields], context=self.screen.context)[0] except RPCException: return date_format = self.screen.context.get('date_format', '%x') datetime_format = date_format + ' %H:%M:%S.%f' message_str = '' for (key, label) in fields: value = data keys = key.split('.') name = keys.pop(-1) for key in keys: value = value.get(key + '.', {}) value = (value or {}).get(name, '/') if isinstance(value, datetime.datetime): value = timezoned_date(value).strftime(datetime_format) message_str += '%s %s\n' % (label, value) message_str += _('Model:') + ' ' + self.model message(message_str) return True def sig_revision(self, widget=None): if not self.modified_save(): return current_id = (self.screen.current_record.id if self.screen.current_record else None) try: revisions = RPCExecute( 'model', self.model, 'history_revisions', [r.id for r in self.screen.selected_records]) except RPCException: return revision = self.screen.context.get('_datetime') format_ = self.screen.context.get('date_format', '%x') format_ += ' %H:%M:%S.%f' revision = Revision(revisions, revision, format_).run() # Prevent too old revision in form view if (self.screen.current_view.view_type == 'form' and revision and revision < revisions[-1][0]): revision = revisions[-1][0] if revision != self.screen.context.get('_datetime'): self.screen.clear() # Update root group context that will be propagated self.screen.group._context['_datetime'] = revision if self.screen.current_view.view_type != 'form': self.screen.search_filter( self.screen.screen_container.get_text()) else: # Test if record exist in revisions self.screen.load([current_id]) self.screen.display(set_cursor=True) self.update_revision() def update_revision(self): tooltips = common.Tooltips() revision = self.screen.context.get('_datetime') if revision: format_ = self.screen.context.get('date_format', '%x') format_ += ' %H:%M:%S.%f' revision_label = ' @ %s' % revision.strftime(format_) label = common.ellipsize(self.name, 80 - len(revision_label)) + revision_label tooltip = self.name + revision_label else: label = common.ellipsize(self.name, 80) tooltip = self.name self.title.set_markup(label) tooltips.set_tip(self.title, tooltip) self.set_buttons_sensitive(revision) def set_buttons_sensitive(self, revision=None): if not revision: access = common.MODELACCESS[self.model] for name, sensitive in [ ('new', access['create']), ('save', access['create'] or access['write']), ('remove', access['delete']), ('copy', access['create']), ('import', access['create']), ]: if name in self.buttons: self.buttons[name].props.sensitive = sensitive if name in self.menu_buttons: self.menu_buttons[name].props.sensitive = sensitive else: for name in ['new', 'save', 'remove', 'copy', 'import']: if name in self.buttons: self.buttons[name].props.sensitive = False if name in self.menu_buttons: self.menu_buttons[name].props.sensitive = False def sig_remove(self, widget=None): if not common.MODELACCESS[self.model]['delete']: return if self.screen.current_view.view_type == 'form': msg = _('Are you sure to remove this record?') else: msg = _('Are you sure to remove those records?') if sur(msg): if not self.screen.remove(delete=True, force_remove=True): self.message_info(_('Records not removed.'), Gtk.MessageType.ERROR) else: self.message_info(_('Records removed.'), Gtk.MessageType.INFO) self.screen.count_tab_domain() def sig_import(self, widget=None): WinImport(self.title.get_text(), self.model, self.screen.context) def sig_export(self, widget=None): if not self.modified_save(): return export = WinExport(self.title.get_text(), self.model, [r.id for r in self.screen.selected_records], context=self.screen.context) for name in self.screen.current_view.get_fields(): type = self.screen.group.fields[name].attrs['type'] if type == 'selection': export.sel_field(name + '.translated') elif type == 'reference': export.sel_field(name + '.translated') export.sel_field(name + '/rec_name') else: export.sel_field(name) def do_export(self, widget, export): if not self.modified_save(): return ids = [r.id for r in self.screen.selected_records] fields = [f['name'] for f in export['export_fields.']] data = RPCExecute('model', self.model, 'export_data', ids, fields, context=self.screen.context) delimiter = ',' if os.name == 'nt' and ',' == locale.localeconv()['decimal_point']: delimiter = ';' fileno, fname = tempfile.mkstemp('.csv', common.slugify(export['name']) + '_') with open(fname, 'w') as fp: writer = csv.writer(fp, delimiter=delimiter) writer.writerow(fields) for row in data: writer.writerow(WinExport.format_row(row)) os.close(fileno) common.file_open(fname, 'csv') def sig_new(self, widget=None, autosave=True): if not common.MODELACCESS[self.model]['create']: return if autosave: if not self.modified_save(): return self.screen.new() self.message_info() self.activate_save() def sig_copy(self, widget=None): if not common.MODELACCESS[self.model]['create']: return if not self.modified_save(): return if self.screen.copy(): self.message_info(_('Working now on the duplicated record(s).'), Gtk.MessageType.INFO) self.screen.count_tab_domain() def sig_save(self, widget=None): if widget: # Called from button so we must save the tree state self.screen.save_tree_state() if not (common.MODELACCESS[self.model]['write'] or common.MODELACCESS[self.model]['create']): return if self.screen.save_current(): self.message_info(_('Record saved.'), Gtk.MessageType.INFO) self.screen.count_tab_domain() return True else: self.message_info(self.screen.invalid_message(), Gtk.MessageType.ERROR) return False def sig_previous(self, widget=None): if not self.modified_save(): return self.screen.display_prev() self.message_info() self.activate_save() def sig_next(self, widget=None): if not self.modified_save(): return self.screen.display_next() self.message_info() self.activate_save() def sig_reload(self, test_modified=True): if test_modified: if not self.modified_save(): return False else: self.screen.save_tree_state(store=False) self.screen.cancel_current() set_cursor = False record_id = (self.screen.current_record.id if self.screen.current_record else None) if self.screen.current_view.view_type != 'form': self.screen.search_filter(self.screen.screen_container.get_text()) for record in self.screen.group: if record.id == record_id: self.screen.current_record = record set_cursor = True break self.screen.display(set_cursor=set_cursor) self.message_info() self.activate_save() self.screen.count_tab_domain() return True def sig_action(self, widget): if self.buttons['action'].props.sensitive: self.buttons['action'].props.active = True def sig_print(self, widget): if self.buttons['print'].props.sensitive: self.buttons['print'].props.active = True def sig_print_open(self, widget): if self.buttons['open'].props.sensitive: self.buttons['open'].props.active = True def sig_print_email(self, widget): if self.buttons['email'].props.sensitive: self.buttons['email'].props.active = True def sig_relate(self, widget): if self.buttons['relate'].props.sensitive: self.buttons['relate'].props.active = True def sig_copy_url(self, widget): if self.buttons['copy_url'].props.sensitive: self.buttons['copy_url'].props.active = True def sig_search(self, widget): search_container = self.screen.screen_container if hasattr(search_container, 'search_entry'): search_container.search_entry.grab_focus() def action_popup(self, widget): button, = widget.get_children() button.grab_focus() menu = widget._menu if not widget.props.active: menu.popdown() return def menu_position(menu, x, y, user_data): widget_allocation = widget.get_allocation() x, y = widget.get_window().get_root_coords(widget_allocation.x, widget_allocation.y) return (x, y + widget_allocation.height, False) menu.show_all() if hasattr(menu, 'popup_at_widget'): menu.popup_at_widget(widget, Gdk.Gravity.SOUTH_WEST, Gdk.Gravity.NORTH_WEST, Gtk.get_current_event()) else: menu.popup(None, None, menu_position, None, 0, Gtk.get_current_event_time()) def _record_message(self, screen, signal_data): name = '_' if signal_data[0]: name = str(signal_data[0]) for button_id in ('print', 'relate', 'email', 'open', 'save', 'attach'): button = self.buttons[button_id] can_be_sensitive = getattr(button, '_can_be_sensitive', True) if button_id in {'print', 'relate', 'email', 'open'}: action_type = button_id if button_id in {'email', 'open'}: action_type = 'print' can_be_sensitive |= any( b.attrs.get('keyword', 'action') == action_type for b in screen.get_buttons()) button.props.sensitive = (bool(signal_data[0]) and can_be_sensitive) button_switch = self.buttons['switch'] button_switch.props.sensitive = self.screen.number_of_views > 1 msg = name + ' / ' + str(signal_data[1]) if signal_data[1] < signal_data[2]: msg += _(' of ') + str(signal_data[2]) self.status_label.set_text(msg) self.message_info() self.activate_save() def _record_modified(self, screen, signal_data): # As it is called via idle_add, the form could have been destroyed in # the meantime. if self.widget_get().props.window: self.activate_save() def _record_saved(self, screen, signal_data): self.activate_save() self.refresh_resources() def modified_save(self): self.screen.save_tree_state() self.screen.current_view.set_value() if self.screen.modified(): value = sur_3b( _('This record has been modified\n' 'do you want to save it?')) if value == 'ok': return self.sig_save(None) if value == 'ko': return self.sig_reload(test_modified=False) return False return True def sig_close(self, widget=None): for dialog in reversed(self.dialogs[:]): dialog.destroy() return self.modified_save() def _action(self, action, atype): if not self.modified_save(): return action = action.copy() record_id = (self.screen.current_record.id if self.screen.current_record else None) record_ids = [r.id for r in self.screen.selected_records] action = Action.evaluate(action, atype, self.screen.current_record) data = { 'model': self.screen.model_name, 'id': record_id, 'ids': record_ids, } Action.execute(action, data, context=self.screen.local_context) def activate_save(self): self.buttons['save'].props.sensitive = self.screen.modified() def sig_win_close(self, widget): Main().sig_win_close(widget) def create_toolbar(self, toolbars): gtktoolbar = super(Form, self).create_toolbar(toolbars) attach_btn = self.buttons['attach'] attach_btn.drag_dest_set(Gtk.DestDefaults.ALL, [ Gtk.TargetEntry.new('text/uri-list', 0, 0), Gtk.TargetEntry.new('text/plain', 0, 0), ], Gdk.DragAction.MOVE | Gdk.DragAction.COPY) attach_btn.connect('drag_data_received', self.attach_drag_data_received) iconstock = { 'print': 'tryton-print', 'action': 'tryton-launch', 'relate': 'tryton-link', 'email': 'tryton-email', 'open': 'tryton-open', } for action_type, special_action, action_name, tooltip in ( ('action', 'action', _('Action'), _('Launch action')), ('relate', 'relate', _('Relate'), _('Open related records')), (None, ) * 4, ('print', 'open', _('Report'), _('Open report')), ('print', 'email', _('E-Mail'), _('E-Mail report')), ('print', 'print', _('Print'), _('Print report')), ): if action_type is not None: tbutton = Gtk.ToggleToolButton() tbutton.set_icon_widget( common.IconFactory.get_image(iconstock.get(special_action), Gtk.IconSize.LARGE_TOOLBAR)) tbutton.set_label(action_name) tbutton._menu = self._create_popup_menu( tbutton, action_type, toolbars[action_type], special_action) tbutton.connect('toggled', self.action_popup) self.tooltips.set_tip(tbutton, tooltip) self.buttons[special_action] = tbutton if action_type != 'action': tbutton._can_be_sensitive = bool( tbutton._menu.get_children()) else: tbutton = Gtk.SeparatorToolItem() gtktoolbar.insert(tbutton, -1) exports = toolbars['exports'] if exports: tbutton = self.buttons['open'] tbutton._can_be_sensitive = True menu = tbutton._menu if menu.get_children(): menu.add(Gtk.SeparatorMenuItem()) for export in exports: menuitem = Gtk.MenuItem(set_underline(export['name'])) menuitem.set_use_underline(True) menuitem.connect('activate', self.do_export, export) menu.add(menuitem) gtktoolbar.insert(Gtk.SeparatorToolItem(), -1) url_button = Gtk.ToggleToolButton() url_button.set_icon_widget( common.IconFactory.get_image('tryton-public', Gtk.IconSize.LARGE_TOOLBAR)) url_button.set_label(_('_Copy URL')) url_button.set_use_underline(True) self.tooltips.set_tip(url_button, _('Copy URL into clipboard')) url_button._menu = url_menu = Gtk.Menu() url_menuitem = Gtk.MenuItem() url_menuitem.connect('activate', self.url_copy) url_menu.add(url_menuitem) url_menu.show_all() url_menu.connect('deactivate', self._popup_menu_hide, url_button) url_button.connect('toggled', self.url_set, url_menuitem) url_button.connect('toggled', self.action_popup) self.buttons['copy_url'] = url_button gtktoolbar.insert(url_button, -1) return gtktoolbar def _create_popup_menu(self, widget, keyword, actions, special_action): menu = Gtk.Menu() menu.connect('deactivate', self._popup_menu_hide, widget) widget.connect('toggled', self._update_popup_menu, menu, keyword) for action in actions: new_action = action.copy() if special_action == 'print': new_action['direct_print'] = True elif special_action == 'email': new_action['email_print'] = True menuitem = Gtk.MenuItem(label=set_underline(action['name'])) menuitem.set_use_underline(True) menuitem.connect('activate', self._popup_menu_selected, widget, new_action, keyword) menu.add(menuitem) return menu def _popup_menu_selected(self, menuitem, togglebutton, action, keyword): event = Gtk.get_current_event() allow_similar = False if (event.state & Gdk.ModifierType.CONTROL_MASK or event.state & Gdk.ModifierType.MOD1_MASK): allow_similar = True with Window(hide_current=True, allow_similar=allow_similar): self._action(action, keyword) togglebutton.props.active = False def _popup_menu_hide(self, menuitem, togglebutton): togglebutton.props.active = False def _update_popup_menu(self, tbutton, menu, keyword): for item in menu.get_children(): if getattr(item, '_update_action', False): menu.remove(item) buttons = [ b for b in self.screen.get_buttons() if keyword == b.attrs.get('keyword', 'action') ] if buttons and menu.get_children(): separator = Gtk.SeparatorMenuItem() separator._update_action = True menu.add(separator) for button in buttons: menuitem = Gtk.MenuItem(label=set_underline( button.attrs.get('string', _('Unknown'))), use_underline=True) menuitem.connect('activate', lambda m, attrs: self.screen.button(attrs), button.attrs) menuitem._update_action = True menu.add(menuitem) kw_plugins = [] for plugin in plugins.MODULES: for plugin_spec in plugin.get_plugins(self.model): name, func = plugin_spec[:2] try: plugin_keyword = plugin_spec[2] except IndexError: plugin_keyword = 'action' if keyword != plugin_keyword: continue kw_plugins.append((name, func)) if kw_plugins: separator = Gtk.SeparatorMenuItem() separator._update_action = True menu.add(separator) for name, func in kw_plugins: menuitem = Gtk.MenuItem(label=set_underline(name)) menuitem.set_use_underline(True) menuitem.connect( 'activate', lambda m, func: func({ 'model': self.model, 'ids': [r.id for r in self.screen.selected_records], 'id': (self.screen.current_record.id if self.screen.current_record else None), }), func) menuitem._update_action = True menu.add(menuitem) def url_copy(self, menuitem): url = self.screen.get_url(self.name) for selection in [ Gdk.Atom.intern('PRIMARY', True), Gdk.Atom.intern('CLIPBOARD', True), ]: clipboard = Gtk.Clipboard.get(selection) clipboard.set_text(url, -1) def url_set(self, button, menuitem): url = self.screen.get_url(self.name) size = 80 if len(url) > size: url = url[:size // 2] + '...' + url[-size // 2:] menuitem.set_label(url) def set_cursor(self): if self.screen: self.screen.set_cursor(reset_view=False) def attach_drag_data_received(self, widget, context, x, y, selection, info, timestamp): record = self.screen.current_record if not record or record.id < 0: return win_attach = Attachment(record, lambda: self.refresh_resources(reload=True)) if info == 0: if selection.get_uris(): for uri in selection.get_uris(): # Win32 cut&paste terminates the list with a NULL character if not uri or uri == '\0': continue win_attach.add_uri(uri) else: win_attach.add_uri(selection.get_text())
class Action(SignalEvent): def __init__(self, attrs=None, context=None): super(Action, self).__init__() self.act_id = int(attrs['name']) self.context = context or {} try: self.action = RPCExecute('model', 'ir.action.act_window', 'read', self.act_id, False) except RPCException: raise view_ids = None self.action['view_mode'] = None if self.action.get('views', []): view_ids = [x[0] for x in self.action['views']] self.action['view_mode'] = [x[1] for x in self.action['views']] elif self.action.get('view_id', False): view_ids = [self.action['view_id'][0]] if 'view_mode' in attrs: self.action['view_mode'] = attrs['view_mode'] self.action.setdefault('pyson_domain', '[]') self.context.update(rpc.CONTEXT) self.context['_user'] = rpc._USER self.context.update(PYSONDecoder(self.context).decode( self.action.get('pyson_context', '{}'))) eval_ctx = self.context.copy() self.context.update(PYSONDecoder(eval_ctx).decode( self.action.get('pyson_context', '{}'))) self.domain = [] self.update_domain([]) search_context = self.context.copy() search_context['context'] = self.context search_context['_user'] = rpc._USER search_value = PYSONDecoder(search_context).decode( self.action['pyson_search_value'] or '{}') self.widget = gtk.Frame() self.widget.set_border_width(0) vbox = gtk.VBox(homogeneous=False, spacing=3) self.widget.add(vbox) self.title = gtk.Label() self.widget.set_label_widget(self.title) self.widget.set_label_align(0.0, 0.5) self.widget.show_all() self.screen = Screen(self.action['res_model'], mode=self.action['view_mode'], context=self.context, view_ids=view_ids, domain=self.domain, search_value=search_value, row_activate=self.row_activate) vbox.pack_start(self.screen.widget, expand=True, fill=True) name = self.screen.current_view.title self.screen.signal_connect(self, 'record-message', self._active_changed) if attrs.get('string'): self.title.set_text(attrs['string']) elif self.action.get('window_name'): self.title.set_text(self.action['name']) else: self.title.set_text(name) self.widget.set_size_request(int(attrs.get('width', -1)), int(attrs.get('height', -1))) self.screen.search_filter() def row_activate(self): if not self.screen.current_record: return def callback(result): if result: self.screen.current_record.save() else: self.screen.current_record.cancel() WinForm(self.screen, callback) def set_value(self, mode, model_field): self.screen.current_view.set_value() return True def display(self): self.screen.search_filter(self.screen.screen_container.get_text()) def _active_changed(self, *args): self.signal('active-changed') def _get_active(self): if self.screen and self.screen.current_record: return common.EvalEnvironment(self.screen.current_record, False) active = property(_get_active) def update_domain(self, actions): domain_ctx = self.context.copy() domain_ctx['context'] = domain_ctx domain_ctx['_user'] = rpc._USER for action in actions: if action.active: domain_ctx['_active_%s' % action.act_id] = action.active new_domain = PYSONDecoder(domain_ctx).decode( self.action['pyson_domain']) if self.domain == new_domain: return del self.domain[:] self.domain.extend(new_domain) if hasattr(self, 'screen'): # Catch early update self.display()
def __init__(self, view, attrs): super(One2Many, self).__init__(view, attrs) self.widget = gtk.Frame() self.widget.set_shadow_type(gtk.SHADOW_NONE) self.widget.get_accessible().set_name(attrs.get('string', '')) vbox = gtk.VBox(homogeneous=False, spacing=2) self.widget.add(vbox) self._readonly = True self._required = False self._position = 0 self._length = 0 self.title_box = hbox = gtk.HBox(homogeneous=False, spacing=0) hbox.set_border_width(2) self.title = gtk.Label(attrs.get('string', '')) self.title.set_alignment(0.0, 0.5) hbox.pack_start(self.title, expand=True, fill=True) hbox.pack_start(gtk.VSeparator(), expand=False, fill=True) tooltips = common.Tooltips() self.focus_out = True self.wid_completion = None if attrs.get('add_remove'): self.wid_text = PlaceholderEntry() self.wid_text.set_placeholder_text(_('Search')) self.wid_text.set_property('width_chars', 13) self.wid_text.connect('focus-out-event', lambda *a: self._focus_out()) hbox.pack_start(self.wid_text, expand=True, fill=True) if int(self.attrs.get('completion', 1)): access = common.MODELACCESS[attrs['relation']] self.wid_completion = get_completion( search=access['read'] and access['write'], create=attrs.get('create', True) and access['create']) self.wid_completion.connect('match-selected', self._completion_match_selected) self.wid_completion.connect('action-activated', self._completion_action_activated) self.wid_text.set_completion(self.wid_completion) self.wid_text.connect('changed', self._update_completion) self.but_add = gtk.Button() tooltips.set_tip(self.but_add, _('Add existing record')) self.but_add.connect('clicked', self._sig_add) img_add = gtk.Image() img_add.set_from_stock('tryton-list-add', gtk.ICON_SIZE_SMALL_TOOLBAR) img_add.set_alignment(0.5, 0.5) self.but_add.add(img_add) self.but_add.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_add, expand=False, fill=False) self.but_remove = gtk.Button() tooltips.set_tip(self.but_remove, _('Remove selected record')) self.but_remove.connect('clicked', self._sig_remove, True) img_remove = gtk.Image() img_remove.set_from_stock('tryton-list-remove', gtk.ICON_SIZE_SMALL_TOOLBAR) img_remove.set_alignment(0.5, 0.5) self.but_remove.add(img_remove) self.but_remove.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_remove, expand=False, fill=False) hbox.pack_start(gtk.VSeparator(), expand=False, fill=True) self.but_new = gtk.Button() tooltips.set_tip(self.but_new, _('Create a new record <F3>')) self.but_new.connect('clicked', self._sig_new) img_new = gtk.Image() img_new.set_from_stock('tryton-new', gtk.ICON_SIZE_SMALL_TOOLBAR) img_new.set_alignment(0.5, 0.5) self.but_new.add(img_new) self.but_new.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_new, expand=False, fill=False) self.but_open = gtk.Button() tooltips.set_tip(self.but_open, _('Edit selected record <F2>')) self.but_open.connect('clicked', self._sig_edit) img_open = gtk.Image() img_open.set_from_stock('tryton-open', gtk.ICON_SIZE_SMALL_TOOLBAR) img_open.set_alignment(0.5, 0.5) self.but_open.add(img_open) self.but_open.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_open, expand=False, fill=False) self.but_del = gtk.Button() tooltips.set_tip(self.but_del, _('Delete selected record <Del>')) self.but_del.connect('clicked', self._sig_remove, False) img_del = gtk.Image() img_del.set_from_stock('tryton-delete', gtk.ICON_SIZE_SMALL_TOOLBAR) img_del.set_alignment(0.5, 0.5) self.but_del.add(img_del) self.but_del.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_del, expand=False, fill=False) self.but_undel = gtk.Button() tooltips.set_tip(self.but_undel, _('Undelete selected record <Ins>')) self.but_undel.connect('clicked', self._sig_undelete) img_undel = gtk.Image() img_undel.set_from_stock('tryton-undo', gtk.ICON_SIZE_SMALL_TOOLBAR) img_undel.set_alignment(0.5, 0.5) self.but_undel.add(img_undel) self.but_undel.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_undel, expand=False, fill=False) hbox.pack_start(gtk.VSeparator(), expand=False, fill=True) self.but_pre = gtk.Button() tooltips.set_tip(self.but_pre, _('Previous')) self.but_pre.connect('clicked', self._sig_previous) img_pre = gtk.Image() img_pre.set_from_stock('tryton-go-previous', gtk.ICON_SIZE_SMALL_TOOLBAR) img_pre.set_alignment(0.5, 0.5) self.but_pre.add(img_pre) self.but_pre.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_pre, expand=False, fill=False) self.label = gtk.Label('(0,0)') hbox.pack_start(self.label, expand=False, fill=False) self.but_next = gtk.Button() tooltips.set_tip(self.but_next, _('Next')) self.but_next.connect('clicked', self._sig_next) img_next = gtk.Image() img_next.set_from_stock('tryton-go-next', gtk.ICON_SIZE_SMALL_TOOLBAR) img_next.set_alignment(0.5, 0.5) self.but_next.add(img_next) self.but_next.set_relief(gtk.RELIEF_NONE) hbox.pack_start(self.but_next, expand=False, fill=False) hbox.pack_start(gtk.VSeparator(), expand=False, fill=True) but_switch = gtk.Button() tooltips.set_tip(but_switch, _('Switch')) but_switch.connect('clicked', self.switch_view) img_switch = gtk.Image() img_switch.set_from_stock('tryton-fullscreen', gtk.ICON_SIZE_SMALL_TOOLBAR) img_switch.set_alignment(0.5, 0.5) but_switch.add(img_switch) but_switch.set_relief(gtk.RELIEF_NONE) hbox.pack_start(but_switch, expand=False, fill=False) if attrs.get('add_remove'): hbox.set_focus_chain([self.wid_text]) else: hbox.set_focus_chain([]) tooltips.enable() frame = gtk.Frame() frame.add(hbox) frame.set_shadow_type(gtk.SHADOW_OUT) vbox.pack_start(frame, expand=False, fill=True) self.screen = Screen(attrs['relation'], mode=attrs.get('mode', 'tree,form').split(','), view_ids=attrs.get('view_ids', '').split(','), views_preload=attrs.get('views', {}), row_activate=self._on_activate, exclude_field=attrs.get('relation_field', None)) self.screen.pre_validate = bool(int(attrs.get('pre_validate', 0))) self.screen.signal_connect(self, 'record-message', self._sig_label) vbox.pack_start(self.screen.widget, expand=True, fill=True) self.screen.widget.connect('key_press_event', self.on_keypress) if self.attrs.get('add_remove'): self.wid_text.connect('key_press_event', self.on_keypress) but_switch.props.sensitive = self.screen.number_of_views > 1
def __init__(self, attrs=None, context=None): super(Action, self).__init__() self.act_id = int(attrs['name']) self.context = context or {} try: self.action = RPCExecute('model', 'ir.action.act_window', 'read', self.act_id, False) except RPCException: raise view_ids = None self.action['view_mode'] = None if self.action.get('views', []): view_ids = [x[0] for x in self.action['views']] self.action['view_mode'] = [x[1] for x in self.action['views']] elif self.action.get('view_id', False): view_ids = [self.action['view_id'][0]] if 'view_mode' in attrs: self.action['view_mode'] = attrs['view_mode'] self.action.setdefault('pyson_domain', '[]') self.context.update(rpc.CONTEXT) self.context['_user'] = rpc._USER self.context.update(PYSONDecoder(self.context).decode( self.action.get('pyson_context', '{}'))) eval_ctx = self.context.copy() self.context.update(PYSONDecoder(eval_ctx).decode( self.action.get('pyson_context', '{}'))) self.domain = [] self.update_domain([]) search_context = self.context.copy() search_context['context'] = self.context search_context['_user'] = rpc._USER search_value = PYSONDecoder(search_context).decode( self.action['pyson_search_value'] or '{}') self.widget = gtk.Frame() self.widget.set_border_width(0) vbox = gtk.VBox(homogeneous=False, spacing=3) self.widget.add(vbox) self.title = gtk.Label() self.widget.set_label_widget(self.title) self.widget.set_label_align(0.0, 0.5) self.widget.show_all() self.screen = Screen(self.action['res_model'], mode=self.action['view_mode'], context=self.context, view_ids=view_ids, domain=self.domain, search_value=search_value, row_activate=self.row_activate) vbox.pack_start(self.screen.widget, expand=True, fill=True) name = self.screen.current_view.title self.screen.signal_connect(self, 'record-message', self._active_changed) if attrs.get('string'): self.title.set_text(attrs['string']) elif self.action.get('window_name'): self.title.set_text(self.action['name']) else: self.title.set_text(name) self.widget.set_size_request(int(attrs.get('width', -1)), int(attrs.get('height', -1))) self.screen.search_filter()