Merge tree and list views

It adds also Drag & Drop on tree and a new UI layout.
The menu is now on a left side and the toolbar is inside tab.
This commit is contained in:
Cédric Krier committed 2011-04-03 14:59:34 +02:00
1 parent 2457573f7b
commit 5d3d16cbdf
38 files changed
+1360 -1950

No files matched your search

+3
View File
@@ -1,3 +1,6 @@
* New UI layout
* Add DnD on tree view
* Merge tree and list views
* Remove generic default value on right-click
* Made numpad locale aware
* Selection widget used for many2one dynamically change their content following
+253 -322
View File
@@ -80,35 +80,35 @@ all important visual parts.
Figure: Tryton client application::
Client Window _______________________________________________________
| Tryton _ o x|
|-------------------------------------------------------|
Menu bar | File User Form Options Plugins Shortcuts Help |
|_______________________________________________________|
| |
Tool bar | New Save | Delete | Find Previous Next Switch v |
|-------------------------------------------------------|
| ______ |
Tab bar | [Menu] |[Tab1]| [Tab2] ... |
|---------| | -------------------------------------|
| .------- ------------------------------------. |
| | | |
| | | |
| | | |
| | | |
| | | |
View | | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| |___________________________________________________| |
|_______________________________________________________|
Status bar |username company Waiting requests... login@...|
|_______________________________________________________|
Client Window ________________________________________________________________
| Tryton _ o x|
|----------------------------------------------------------------|
Menu bar | File User Options Plugins Shortcuts Help |
|________________________________________________________________|
| | ______ |
Tabs | Menu | [Tab1] |[Tab2]| [Tab3]... |
|-------------| +-------+ +--------------------------------+|
| + | | Tab2 ||
| |-+ | |-----------------------------------------------||
Tool bar | | |- | | New Save|Delete|Find Previous Next Switch v ||
| | |- | |-----------------------------------------------||
| + | | ||
| |-+ | | ||
| | |- | | ||
| | |- | | ||
| + | | ||
View | |-+ | | ||
| |- | | ||
| |- | | ||
| | | ||
| | | ||
| | | ||
| | | ||
| | | ||
|_____________| |_______________________________________________||
|________________________________________________________________|
Status bar |username company Waiting requests... login@...|
|________________________________________________________________|
Menu Bar
@@ -149,32 +149,6 @@ contextual menu for some views and fields. In this context menu the user finds
:term:`actions` to copy and paste contents or selections for several fields.
Tool Bar
^^^^^^^^
The tool bar contains some often used menu functions mapped to icons.
In addition to the menu bar the tool bar contains a button called
*Attachment*.
Attachment
++++++++++
The attachment item handles the document management system of
Tryton which is able to attach files to any arbitrary :term:`model`. On click
it opens the attachments :term:`dialog`. The default dialog shows a list view
of the attached files and links.
A single Attachment has the following Options:
* Attachment Name: The name of the attachment.
* Datas: The attached File. The file size is displayed as text.
* Link: Link to an external site as URL.
* Preview tab: Shows a preview picture of the selected attachment.
* Description tab: Shows and and provides editing of a free text description
for the selected attachment.
* Actions:
- OK: Closes the Dialog and saves the attachment(s).
Tabbed Main Frame
^^^^^^^^^^^^^^^^^
This part of the client contains all the related contents and
@@ -195,74 +169,22 @@ __ TDI_
.. Note:: Inside :term:`views` there can be tabs, too.
Main Menu
+++++++++
The first left tab contains the *main menu* (... not to mix up with
the menu bar!). The *main menu* does not contain fixed menu items.
Menu
++++
The *menu* does not contain fixed menu items.
All of them are dynamically provided by the actual set of the installed
:term:`modules` depending on the access rules of the current user. If a menu
item is clicked, the appropriate action will open in a new tab. As the figure
below illustrates, the *main menu* is split up in three frames.
The upper left frame contains the first or top level entries of the *main
menu*. Standard top level entries are 'Administration' and 'Tryton'.
.. note:: The *administration* menu will be explained in the modules
documentation for the 'IR' module, the *information repository*.
The top level entry 'Tryton' connects to the website of the Tryton project.
The right frame is showing a :term:`tree view` substructure of menu *items*
and *headings*. With the arrow keys it is possible to navigate inside the menu.
By pressing *Enter* or double-clicking onto the menu item the appropriate
:term:`views` open. Use left and right arrow to expand or contract sub
items of a heading.
Figure: Main Menu Tab::
________________________________________________________
| Tryton _ o x|
|--------------------------------------------------------|
| File User Form Options Plugins Shortcuts Help |
|________________________________________________________|
| |
| New Save | Delete | Find Previous Next Switch v |
|________________________________________________________|
| ______ |
| |[Menu]| [Tab1] [Tab2] ... |
|--| | ---------------------------------------------|
| | -------------.------------------------------. |
| | Top Level Entry 1 | Menu | | |
| | Top Level Entry 2 |---------------------+--------| |
| | ... | Item 1 | | |
| | Administration | Item 2 | | |
| | Tryton | > Heading | | |
| |____________________| Sub Item 1 | | |
| | Shortcuts [+][-]| Sub Item 2 | | |
| |--------------------| > Sub Heading | | |
| | Sub Item 2 | Sub Sub ... | | |
| | | | | |
| | | | | |
| |____________________|_____________________|________| |
|________________________________________________________|
|username company Waiting requests... login@... |
|________________________________________________________|
item is clicked, the appropriate action will open in a new tab.
The lower left menu frame shows a user adjustable *shortcuts* menu. This menu
is for collecting often used menu items. Using a *shortcut* item will open
the appropriate view in a new tab, just with a double mouse click.
A menu item is added to the *shortcut* menu by pushing the plus button [+]
in the *shortcut* menu. The minus button [-] removes a selected *shortcut*
item from the list.
Home Action
+++++++++++
Another tab opens during the startup of the Tryton client: the home action.
It is usually an item of the `Main Menu`_ opening when the user calls his
Home
++++
A tab opens during the startup of the Tryton client: the home. It is
usually an item of the `Menu`_ opening when the user calls his
`Home`__ action defined in the `preferences`__.
__ Menu-Form-Home_
__ Menu-User-Home_
__ Menu-User-Preferences_
@@ -489,21 +411,34 @@ Preferences...
* Email: Email address of the Tryton user.
* Signature: Signature block for the Tryton user.
* Menu Action: Defines the action which is called as the
`main menu`_.
* Home Action: Defines the action which is called as `home action`__.
`Menu`_.
* Home Action: Defines the action which is called as `Home`__.
* Language: Language of the client interface.
* Timezone: The local timezone where the user/client resides.
* Groups: Displays the users membership to access groups.
__ Menu-Form-Home_
.. _Menu-User-Menu-Reload:
.. _Menu-user-send-a-request:
Menu Reload:
Reload the menu.
.. _Menu-User-Menu-Toggle:
Menu Toggle:
Toggle the menu visibility
.. _Menu-User-Home:
Home:
Opens a new `Home`__ tab.
.. _Menu-User-send-a-request:
Send a Request
Opens a tab in :term:`form view` which enables the user to send
requests to other users of the same database.
.. _Menu-user-read-my-request:
.. _Menu-User-read-my-request:
Read my Requests
Opens a tab in :term:`tree view` showing all requests related to the
@@ -556,206 +491,6 @@ Read my Requests
Tryton, which is very similar to email.
Form
^^^^
The form menu contains functions for the *actual form* in the tab which
is open. Some operations are working with one record or with a selection of
:term:`records`. In :term:`form view` the actual record is selected for
operations. In :term:`tree view` all selected records are used for operations.
.. _Menu-Form-New:
New:
Creates a new record.
.. _Menu-Form-Save:
Save:
Saves the actual record.
.. _Menu-Form-Duplicate:
Duplicate:
Duplicates the content of the actual record in a newly created record.
.. _Menu-Form-Delete:
Delete:
Deletes the selected or actual record.
.. _Menu-Form-Find:
.. _search_widget:
Find...:
Opens a :term:`dialog` for finding :term:`fields` with search criteria and
operators.
* Search criteria: Defines the aspects to seek for.
* General search operators:
- Equals: Search for results which are exactly the same as the following
term.
- Does Not Equal: Search for results which are different from the following
term.
* Additional search operators on numbers, amounts and strings:
- Contains: Search for results which contain the following term.
- Does Not Contain: Search for results which do not include the
following term.
- Starts With: Search for results beginning with the following term.
- Ends With: Search for results ending with the following term.
* Additional search operators for numbers and amounts:
- Is Between: Search for results inside a range (from - to).
- Is Not Between: Search for results outside a range (from - to).
- Is Different: Same as 'Does Not Equal', see above.
* Advanced Search expander opens additional search criteria.
- Limit: Limits the count of results.
- Offset: Skips a number of results and show only the following.
* Actions:
- Find: Search for results of the given criteria.
- New: Create a new record (used when search was fruitless, to create
quickly a new record).
- Ok: Open the selected results.
- Cancel
.. note:: To search for deactivated records the *Active* search criteria must be
set to *No*.
.. _Menu-Form-Next:
Next:
Goes to the next record in a list (sequence).
.. _Menu-Form-Previous:
Previous:
Goes to the last record in a list (sequence).
.. _Menu-Form-Switch_View:
Switch View:
Switches the actual view aspect to:
* :term:`Form view`
* :term:`Tree view`
* :term:`Graph view`
Not all views provide all aspects.
.. _Menu-Form-Menu:
Menu:
Activates or reopens the menu tab.
.. _Menu-Form-Home:
Home:
Opens a new `home`__ tab.
__ Menu-User-Preferences_
.. _Menu-Form-Close:
Close Tab:
Closes the current tab. A Request :term:`Dialog` opens in case of unsaved
changes.
.. _Menu-Form-Previous_Tab:
Previous Tab:
Shows the previous (left) tab of the actual tab.
.. _Menu-Form-Next_Tab:
Next Tab:
Shows the next (right) tab of the actual tab.
.. _Menu-Form-View_Logs:
View Logs...:
Shows generic information of the current record.
.. _Menu-Form-Go_to_Record_ID:
Go to Record ID...:
Opens specific record id in the current view.
.. _Menu-Form-Reload_Undo:
Reload/Undo:
Reloads the content of the actual tab. Undoes changes, if save request for
the current record is denied.
.. _Menu-Form-Actions:
Actions...:
Shows all actions for the actual view, model and record.
.. _Menu-Form-Print:
Print...:
Shows all print actions for the actual view, model and record.
.. _Menu-Form-Export_Data:
Export Data...:
Export of current/selected records into :term:`CSV`-file or open it in Excel.
* Predefined exports
- Choose preferences of already saved exports.
* All Fields: Fields available from the model.
* Fields to export: Defines the specific fields to export.
* Options:
- Save: Save export as a CSV file.
- Open: Open export in spread sheet application.
* Add field names: Add a header row with field names to the export data.
* Actions:
- Add: Adds selected fields to *Fields to export*.
- Remove: Removes selected fields from *Fields to export*.
- Clear: Removes all fields from *Fields to export*.
- Save Export: Saves field mapping to a *Predefined export* with a name.
- Delete Export: Deletes a selected *Predefined export*.
- Ok: Exports the data (action depending on *Options*).
- Cancel
.. _Menu-Form-Import_Data:
Import Data...:
Import records from :term:`CSV`-file.
* All Fields: Fields available in the model (required fields are marked up).
* Fields to Import: Exact sequence of all columns in the CSV file.
* File to Import: File :term:`dialog` for choosing a CSV file to import.
* CSV Parameters: Setup specific parameters for chosen CSV file.
- Field Separator: Character which separates CSV fields.
- Text Delimiter: Character which encloses text in CSV.
- Encoding: :term:`Character encoding` of CSV file.
- Lines to Skip: Count of lines to skip a headline or another offset.
* Actions:
- Add: Adds fields to *Fields to Import*.
- Remove: Deletes fields from *Fields to Import*.
- Clear: Removes all fields from *Fields to Import*.
- Auto-Detect: Tries to auto detect fields in the CSV *File to Import*.
- Ok: Proceeds the data import.
- Cancel
Options
^^^^^^^
The Options menu sets up several visual and context depending preferences.
@@ -938,6 +673,202 @@ Keyboard Shortcuts...:
About...:
License, Contributors, Authors of Tryton
Tool Bar
********
The tool bar contains the functionalities linked to the current tab.
The tool bar contains functions for the current tab.
Some operations are working with one record or with a selection of
:term:`records`. In :term:`form view` the actual record is selected for
operations. In :term:`tree view` all selected records are used for operations.
.. _Toolbar-New:
New:
Creates a new record.
.. _Toolbar-Save:
Save:
Saves the actual record.
.. _Toolbar-Duplicate:
Duplicate:
Duplicates the content of the actual record in a newly created record.
.. _Toolbar-Delete:
Delete:
Deletes the selected or actual record.
.. _Toolbar-Find:
.. _search_widget:
Find...:
Opens a :term:`dialog` for finding :term:`fields` with search criteria and
operators.
* Search criteria: Defines the aspects to seek for.
* General search operators:
- Equals: Search for results which are exactly the same as the following
term.
- Does Not Equal: Search for results which are different from the following
term.
* Additional search operators on numbers, amounts and strings:
- Contains: Search for results which contain the following term.
- Does Not Contain: Search for results which do not include the
following term.
- Starts With: Search for results beginning with the following term.
- Ends With: Search for results ending with the following term.
* Additional search operators for numbers and amounts:
- Is Between: Search for results inside a range (from - to).
- Is Not Between: Search for results outside a range (from - to).
- Is Different: Same as 'Does Not Equal', see above.
* Advanced Search expander opens additional search criteria.
- Limit: Limits the count of results.
- Offset: Skips a number of results and show only the following.
* Actions:
- Find: Search for results of the given criteria.
- New: Create a new record (used when search was fruitless, to create
quickly a new record).
- Ok: Open the selected results.
- Cancel
.. note:: To search for deactivated records the *Active* search criteria must be
set to *No*.
.. _Toolbar-Next:
Next:
Goes to the next record in a list (sequence).
.. _Toolbar-Previous:
Previous:
Goes to the last record in a list (sequence).
.. _Toolbar-Switch_View:
Switch View:
Switches the actual view aspect to:
* :term:`Form view`
* :term:`Tree view`
* :term:`Graph view`
Not all views provide all aspects.
.. _Toolbar-Close:
Close Tab:
Closes the current tab. A Request :term:`Dialog` opens in case of unsaved
changes.
.. _Toolbar-Previous_Tab:
Previous Tab:
Shows the previous (left) tab of the actual tab.
.. _Toolbar-Next_Tab:
Next Tab:
Shows the next (right) tab of the actual tab.
.. _Toolbar-View_Logs:
View Logs...:
Shows generic information of the current record.
.. _Toolbar-Go_to_Record_ID:
Go to Record ID...:
Opens specific record id in the current view.
.. _Toolbar-Reload_Undo:
Reload/Undo:
Reloads the content of the actual tab. Undoes changes, if save request for
the current record is denied.
.. _Toolbar-Actions:
Actions...:
Shows all actions for the actual view, model and record.
.. _Toolbar-Print:
Print...:
Shows all print actions for the actual view, model and record.
.. _Toolbar-Export_Data:
Export Data...:
Export of current/selected records into :term:`CSV`-file or open it in Excel.
* Predefined exports
- Choose preferences of already saved exports.
* All Fields: Fields available from the model.
* Fields to export: Defines the specific fields to export.
* Options:
- Save: Save export as a CSV file.
- Open: Open export in spread sheet application.
* Add field names: Add a header row with field names to the export data.
* Actions:
- Add: Adds selected fields to *Fields to export*.
- Remove: Removes selected fields from *Fields to export*.
- Clear: Removes all fields from *Fields to export*.
- Save Export: Saves field mapping to a *Predefined export* with a name.
- Delete Export: Deletes a selected *Predefined export*.
- Ok: Exports the data (action depending on *Options*).
- Cancel
.. _Toolbar-Import_Data:
Import Data...:
Import records from :term:`CSV`-file.
* All Fields: Fields available in the model (required fields are marked up).
* Fields to Import: Exact sequence of all columns in the CSV file.
* File to Import: File :term:`dialog` for choosing a CSV file to import.
* CSV Parameters: Setup specific parameters for chosen CSV file.
- Field Separator: Character which separates CSV fields.
- Text Delimiter: Character which encloses text in CSV.
- Encoding: :term:`Character encoding` of CSV file.
- Lines to Skip: Count of lines to skip a headline or another offset.
* Actions:
- Add: Adds fields to *Fields to Import*.
- Remove: Deletes fields from *Fields to Import*.
- Clear: Removes all fields from *Fields to Import*.
- Auto-Detect: Tries to auto detect fields in the CSV *File to Import*.
- Ok: Proceeds the data import.
- Cancel
.. _Toolbar-Attachment:
Attachment:
The attachment item handles the document management system of
Tryton which is able to attach files to any arbitrary :term:`model`.
On click it opens the attachments :term:`dialog`. The default dialog
shows a list view of the attached files and links.
Appendix
********
+9 -6
View File
@@ -142,12 +142,12 @@ class Action(object):
res_model = action.get('res_model', data.get('res_model'))
res_id = action.get('res_id', data.get('res_id'))
Window.create(view_ids, res_model, res_id, domain,
action['view_type'], window, action_ctx, view_mode,
name=name, limit=action.get('limit'),
auto_refresh=action.get('auto_refresh'),
search_value=search_value,
icon=(action.get('icon.rec_name') or ''))
Window.create(view_ids, res_model, res_id, domain, window,
action_ctx, view_mode, name=name,
limit=action.get('limit'),
auto_refresh=action.get('auto_refresh'),
search_value=search_value,
icon=(action.get('icon.rec_name') or ''))
elif action['type'] == 'ir.action.wizard':
if action.get('window', False):
Window.create_wizard(action['wiz_name'], data, window,
@@ -160,6 +160,9 @@ class Action(object):
direct_print=action.get('direct_print', False),
email_print=action.get('email_print', False),
email=action.get('email'), context=context)
if action['wiz_name'] == 'ir.ui.view_sc.add':
from tryton.gui.main import Main
Main.get_main().shortcut_set()
elif action['type'] == 'ir.action.report':
Action.exec_report(action['report_name'], data, window,
+1
View File
@@ -86,6 +86,7 @@ class ConfigManager(object):
'client.limit': 1000,
'roundup.url': 'http://bugs.tryton.org/roundup/',
'roundup.xmlrpc': 'roundup-xmlrpc.tryton.org',
'menu.pane': 200,
}
self.config = {}
self.options = {
+162 -109
View File
@@ -87,6 +87,13 @@ class Main(object):
gtk.gdk.CONTROL_MASK)
gtk.accel_map_add_entry('<tryton>/File/Quit', gtk.keysyms.Q,
gtk.gdk.CONTROL_MASK)
gtk.accel_map_add_entry('<tryton>/User/Menu Reload', gtk.keysyms.T,
gtk.gdk.MOD1_MASK)
gtk.accel_map_add_entry('<tryton>/User/Menu Toggle', gtk.keysyms.T,
gtk.gdk.CONTROL_MASK)
gtk.accel_map_add_entry('<tryton>/User/Home', gtk.keysyms.H,
gtk.gdk.CONTROL_MASK)
gtk.accel_map_add_entry('<tryton>/Form/New', gtk.keysyms.N,
gtk.gdk.CONTROL_MASK)
gtk.accel_map_add_entry('<tryton>/Form/Save', gtk.keysyms.S,
@@ -103,10 +110,6 @@ class Main(object):
0)
gtk.accel_map_add_entry('<tryton>/Form/Switch View', gtk.keysyms.L,
gtk.gdk.CONTROL_MASK)
gtk.accel_map_add_entry('<tryton>/Form/Menu', gtk.keysyms.T,
gtk.gdk.CONTROL_MASK)
gtk.accel_map_add_entry('<tryton>/Form/Home', gtk.keysyms.H,
gtk.gdk.CONTROL_MASK)
gtk.accel_map_add_entry('<tryton>/Form/Close', gtk.keysyms.W,
gtk.gdk.CONTROL_MASK)
gtk.accel_map_add_entry('<tryton>/Form/Previous Tab', gtk.keysyms.Page_Up,
@@ -131,6 +134,7 @@ class Main(object):
self.toolbar = toolbar
toolbar.set_orientation(gtk.ORIENTATION_HORIZONTAL)
toolbar.set_style(gtk.TOOLBAR_BOTH)
self.toolbar_menu = None
self.vbox = gtk.VBox()
self.window.add(self.vbox)
@@ -138,7 +142,6 @@ class Main(object):
self.status_hbox = None
self.menubar = None
self.menuitem_user = None
self.menuitem_form = None
self.menuitem_plugins = None
self.set_statusbar()
@@ -147,18 +150,20 @@ class Main(object):
if self.macapp is not None:
self.macapp.ready()
self.vbox.pack_start(toolbar, False, True)
self.buttons = {}
self._set_toolbar()
self.set_toolbar_label()
self.pane = gtk.HPaned()
self.menu_screen = None
self.vbox.pack_start(self.pane, True, True)
self.notebook = gtk.Notebook()
self.notebook.popup_enable()
self.notebook.set_scrollable(True)
self.notebook.connect_after('switch-page', self._sig_page_changt)
self.vbox.pack_start(self.notebook, True, True)
self.pane.add2(self.notebook)
self.window.show_all()
@@ -225,20 +230,6 @@ class Main(object):
menu_user.set_accel_group(self.accel_group)
menu_user.set_accel_path('<tryton>/User')
menuitem_form = gtk.MenuItem(_('For_m'))
if self.menuitem_form:
menuitem_form.set_sensitive(
self.menuitem_form.get_property('sensitive'))
else:
menuitem_form.set_sensitive(False)
self.menuitem_form = menuitem_form
menubar.add(menuitem_form)
menu_form = self._set_menu_form()
menuitem_form.set_submenu(menu_form)
menu_form.set_accel_group(self.accel_group)
menu_form.set_accel_path('<tryton>/Form')
menuitem_options = gtk.MenuItem(_('_Options'))
menubar.add(menuitem_options)
@@ -281,7 +272,6 @@ class Main(object):
self.macapp.insert_app_menu_item(self.aboutitem,0)
menuitem_file.show_all()
menuitem_user.show_all()
menuitem_form.show_all()
menuitem_options.show_all()
menuitem_plugins.show_all()
menuitem_shortcut.show_all()
@@ -407,6 +397,31 @@ class Main(object):
menu_user.add(gtk.SeparatorMenuItem())
imagemenuitem_menu = gtk.ImageMenuItem(_('_Menu Reload'), self.accel_group)
image = gtk.Image()
image.set_from_stock('tryton-start-here', gtk.ICON_SIZE_MENU)
imagemenuitem_menu.set_image(image)
imagemenuitem_menu.connect('activate', self.sig_win_menu)
imagemenuitem_menu.set_accel_path('<tryton>/User/Menu Reload')
menu_user.add(imagemenuitem_menu)
imagemenuitem_menu_toggle = gtk.ImageMenuItem(_('_Menu Toggle'),
self.accel_group)
imagemenuitem_menu_toggle.connect('activate', lambda *a: self.menu_toggle())
imagemenuitem_menu_toggle.set_accel_path('<tryton>/User/Menu Toggle')
menu_user.add(imagemenuitem_menu_toggle)
imagemenuitem_home = gtk.ImageMenuItem(_('_Home'), self.accel_group)
image = gtk.Image()
image.set_from_stock('tryton-go-home', gtk.ICON_SIZE_MENU)
imagemenuitem_home.set_image(image)
imagemenuitem_home.connect('activate', self.sig_home_new)
imagemenuitem_home.set_accel_path('<tryton>/User/Home')
menu_user.add(imagemenuitem_home)
menu_user.add(gtk.SeparatorMenuItem())
imagemenuitem_send_request = gtk.ImageMenuItem(_('_Send a Request'))
image = gtk.Image()
image.set_from_stock('tryton-mail-message-new', gtk.ICON_SIZE_MENU)
@@ -493,24 +508,8 @@ class Main(object):
imagemenuitem_switch.set_accel_path('<tryton>/Form/Switch View')
menu_form.add(imagemenuitem_switch)
imagemenuitem_menu = gtk.ImageMenuItem(_('_Menu'), self.accel_group)
image = gtk.Image()
image.set_from_stock('tryton-start-here', gtk.ICON_SIZE_MENU)
imagemenuitem_menu.set_image(image)
imagemenuitem_menu.connect('activate', self.sig_win_menu)
imagemenuitem_menu.set_accel_path('<tryton>/Form/Menu')
menu_form.add(imagemenuitem_menu)
menu_form.add(gtk.SeparatorMenuItem())
imagemenuitem_home = gtk.ImageMenuItem(_('_Home'), self.accel_group)
image = gtk.Image()
image.set_from_stock('tryton-go-home', gtk.ICON_SIZE_MENU)
imagemenuitem_home.set_image(image)
imagemenuitem_home.connect('activate', self.sig_home_new)
imagemenuitem_home.set_accel_path('<tryton>/Form/Home')
menu_form.add(imagemenuitem_home)
imagemenuitem_close = gtk.ImageMenuItem(_('_Close Tab'), self.accel_group)
image = gtk.Image()
image.set_from_stock('tryton-close', gtk.ICON_SIZE_MENU)
@@ -589,6 +588,10 @@ class Main(object):
'but_import')
menuitem_import.set_accel_path('<tryton>/Form/Import Data')
menu_form.add(menuitem_import)
menu_form.set_accel_group(self.accel_group)
menu_form.set_accel_path('<tryton>/Form')
menu_form.show_all()
return menu_form
def _set_menu_options(self):
@@ -891,12 +894,6 @@ class Main(object):
toolbutton_reload.connect('clicked', self._sig_child_call, 'but_reload')
self.buttons['but_reload'] = toolbutton_reload
toolbutton_menu = gtk.ToolButton('tryton-start-here')
self.toolbutton_menu = toolbutton_menu
self.toolbar.insert(toolbutton_menu, -1)
toolbutton_menu.connect('clicked', self.sig_win_menu)
self.buttons['but_menu'] = toolbutton_menu
self.toolbar.insert(gtk.SeparatorToolItem(), -1)
toolbutton_action = gtk.ToolButton('tryton-executable')
@@ -916,13 +913,18 @@ class Main(object):
toolbutton_attach.connect('clicked', self._sig_child_call, 'but_attach')
self.buttons['but_attach'] = toolbutton_attach
self.toolbar.insert(gtk.SeparatorToolItem(), -1)
self.menutoolbutton = None
if hasattr(gtk, 'MenuToolButton'):
self.menutoolbutton = gtk.MenuToolButton(None, None)
# Remove the button to keep only the toggle
hbox, = self.menutoolbutton.get_children()
button, toggle = hbox.get_children()
hbox.remove(button)
self.toolbar.insert(self.menutoolbutton, -1)
menu = self._set_menu_form()
self.menutoolbutton.set_menu(menu)
self.toolbar_menu = menu
toolbutton_request = gtk.ToolButton('tryton-mail-message')
self.toolbutton_request = toolbutton_request
self.toolbar.insert(toolbutton_request, -1)
toolbutton_request.connect('clicked', self.sig_request_open)
self.buttons['but_request'] = toolbutton_request
def set_toolbar_label(self):
labels = {
@@ -934,11 +936,9 @@ class Main(object):
'but_next': _('Next'),
'but_switch': _('Switch'),
'but_reload': _('_Reload'),
'but_menu': _('Menu'),
'but_action': _('Action'),
'but_print': _('Print'),
'but_attach': _('Attachment(0)'),
'but_request': _('Request'),
}
tooltips = {
'but_new': _('Create a new record'),
@@ -949,15 +949,17 @@ class Main(object):
'but_next': _('Next Record'),
'but_switch': _('Switch view'),
'but_reload': _('Reload'),
'but_menu': _('Menu'),
'but_action': _('Action'),
'but_print': _('Print'),
'but_attach': _('Add an attachment to the record'),
'but_request': _('Request'),
}
for i in self.buttons:
self.buttons[i].set_label(labels[i])
self.tooltips.set_tip(self.buttons[i], tooltips[i])
if self.toolbar_menu and self.menutoolbutton:
self.toolbar_menu.destroy()
menu = self._set_menu_form()
self.menutoolbutton.set_menu(menu)
@staticmethod
def get_main():
@@ -970,6 +972,34 @@ class Main(object):
'id': action,
'ids': [action],
})
def _add_shortcut(widget):
ids = self.menu_screen.sel_ids_get()
if not ids:
return
try:
values = rpc.execute('model', self.menu_screen.model_name,
'read', ids, ['rec_name'], rpc.CONTEXT)
except Exception, exception:
common.process_exception(exception, self.window)
return
try:
for value in values:
rpc.execute('model', 'ir.ui.view_sc', 'create', {
'name': value['rec_name'],
'res_id': value['id'],
'user_id': rpc._USER,
'resource': self.menu_screen.model_name,
}, rpc.CONTEXT)
except Exception, exception:
common.process_exception(exception, self.window)
self.shortcut_set()
def _manage_shortcut(widget):
Window.create(False, 'ir.ui.view_sc', False,
domain=[('user_id', '=', rpc._USER)],
window=self.window, mode=['tree', 'form'])
if shortcuts is None:
user = rpc._USER
try:
@@ -982,6 +1012,13 @@ class Main(object):
menuitem = gtk.MenuItem(shortcut['name'])
menuitem.connect('activate', _action_shortcut, shortcut['res_id'])
menu.add(menuitem)
menu.add(gtk.MenuItem())
add_shortcut = gtk.MenuItem(_('Add Shortcut'))
add_shortcut.connect('activate', _add_shortcut)
menu.add(add_shortcut)
manage_shortcut = gtk.MenuItem(_('Manage Shortcut'))
manage_shortcut.connect('activate', _manage_shortcut)
menu.add(manage_shortcut)
menu.show_all()
self.menuitem_shortcut.set_submenu(menu)
self.menuitem_shortcut.set_sensitive(True)
@@ -1082,22 +1119,22 @@ class Main(object):
self.shortcut_set()
self.set_statusbar()
self.request_set()
self.sig_reload_menu()
self.sig_win_menu()
CONFIG['client.lang'] = prefs['language']
CONFIG.save()
self.window.present()
return True
def sig_win_close(self, widget):
self._sig_child_call(widget, 'but_close')
self._sig_remove_book(widget,
self.notebook.get_nth_page(self.notebook.get_current_page()))
def sig_request_new(self, widget):
ctx = {}
ctx.update(rpc.CONTEXT)
ctx['active_test'] = False
return Window.create(None, 'res.request', False, [
], 'form', mode=['form', 'tree'], window=self.window,
context=ctx)
return Window.create(None, 'res.request', False, [ ],
mode=['form', 'tree'], window=self.window, context=ctx)
def sig_request_open(self, widget):
ctx = {}
@@ -1111,9 +1148,8 @@ class Main(object):
else:
raise
ids = ids1 + ids2
return Window.create(False, 'res.request', ids, [
], 'form', mode=['tree', 'form'], window=self.window,
context=ctx)
return Window.create(False, 'res.request', ids, [ ],
mode=['tree', 'form'], window=self.window, context=ctx)
def request_set(self, exception=False):
try:
@@ -1129,13 +1165,6 @@ class Main(object):
ids, ids2 = rpc.execute('model', 'res.request', 'request_get',
rpc.CONTEXT)
label = _('Requests (%s/%s)') % (len(ids), len(ids2))
self.buttons['but_request'].set_label(label)
self.tooltips.set_tip(self.buttons['but_request'], label)
if not ids:
self.buttons['but_request'].set_stock_id('tryton-mail-message')
else:
self.buttons['but_request'].set_stock_id(
'tryton-mail-message-new')
message = _('Waiting requests: %s received - %s sent') % (len(ids),
len(ids2))
self.sb_requests.set_text(message)
@@ -1197,12 +1226,9 @@ class Main(object):
return self.sig_login()
if not self.menuitem_shortcut.get_property('sensitive'):
self.shortcut_set()
self.toolbutton_menu.set_sensitive(True)
self.toolbutton_request.set_sensitive(True)
self.menuitem_user.set_sensitive(True)
self.menuitem_form.set_sensitive(True)
#self.menuitem_form.set_sensitive(True)
self.menuitem_plugins.set_sensitive(True)
self.notebook.grab_focus()
if CONFIG.arguments:
url = CONFIG.arguments.pop()
self.open_url(url)
@@ -1211,7 +1237,7 @@ class Main(object):
def sig_logout(self, widget, disconnect=True):
res = True
while res:
wid = self._wid_get()
wid = self.get_page()
if wid:
if 'but_close' in wid.handlers:
res = wid.handlers['but_close']()
@@ -1220,15 +1246,19 @@ class Main(object):
res = self._win_del()
else:
res = False
if self.pane.get_child1():
self.pane.remove(self.pane.get_child1())
if self.pane.get_position():
CONFIG['menu.pane'] = self.pane.get_position()
if self.menu_screen:
self.menu_screen.destroy()
self.menu_screen = None
self.sb_username.set_text('')
self.sb_servername.set_text('')
self.sb_requests.set_text('')
self.shortcut_unset()
self.toolbutton_menu.set_sensitive(False)
self.toolbutton_request.set_sensitive(False)
self.toolbutton_request.set_label(_('Request'))
self.menuitem_user.set_sensitive(False)
self.menuitem_form.set_sensitive(False)
#self.menuitem_form.set_sensitive(False)
self.menuitem_plugins.set_sensitive(False)
if disconnect:
rpc.logout()
@@ -1256,25 +1286,32 @@ class Main(object):
def sig_shortcuts(self, widget):
Shortcuts(self.window).run()
def sig_reload_menu(self):
res = False
for page in range(len(self.pages)):
if self.pages[page].model == 'ir.ui.menu':
self.pages[page].sig_reload()
hbox = self.notebook.get_tab_label(self.pages[page].widget)
for child in hbox.get_children():
if isinstance(child, gtk.Label):
child.set_text(_('Menu'))
break
res = True
return res
def menu_toggle(self, nohide=False):
has_focus = True
if (self.menu_screen
and self.menu_screen.current_view.view_type == 'tree'):
try:
has_focus = self.menu_screen.current_view.widget_tree.has_focus()
except AttributeError:
has_focus = (self.menu_screen.current_view.widget_tree.flags()
& gtk.HAS_FOCUS)
if self.pane.get_position() and has_focus:
CONFIG['menu.pane'] = self.pane.get_position()
if not nohide:
self.pane.set_position(0)
self.notebook.grab_focus()
else:
self.pane.set_position(int(CONFIG['menu.pane']))
if self.menu_screen:
self.menu_screen.current_view.set_cursor()
def sig_win_menu(self, widget=None, quiet=True, prefs=None):
for page in range(len(self.pages)):
if self.pages[page].model == 'ir.ui.menu':
page_num = self.notebook.page_num(self.pages[page].widget)
self.notebook.set_current_page(page_num)
return True
if self.pane.get_child1():
self.pane.remove(self.pane.get_child1())
if self.pane.get_position():
CONFIG['menu.pane'] = self.pane.get_position()
self.menu_screen = None
self.menu_toggle(nohide=True)
res = self.sig_win_new(widget, menu_type='menu', quiet=quiet,
prefs=prefs)
return res
@@ -1360,6 +1397,16 @@ class Main(object):
return False
def win_add(self, page):
if page.model == 'ir.ui.menu' and not self.pane.get_child1():
screen = page.screen
screen.screen_container.alternate_view = True
screen.switch_view(view_type=screen.current_view.view_type)
self.pane.pack1(screen.screen_container.alternate_viewport)
self.menu_screen = screen
screen.display(set_cursor=True)
return
elif page.model == 'ir.ui.menu':
self.sig_win_menu()
previous_page_id = self.notebook.get_current_page()
previous_widget = self.notebook.get_nth_page(previous_page_id)
self.previous_pages[page] = previous_widget
@@ -1375,8 +1422,6 @@ class Main(object):
else:
noise_size = icon_w + 3
name = page.name
if page.model == 'ir.ui.menu':
name = _('Menu')
label = gtk.Label(name)
self.tooltips.set_tip(label, page.name)
self.tooltips.enable()
@@ -1418,10 +1463,8 @@ class Main(object):
def sb_set(self, view=None):
if not view:
view = self._wid_get()
view = self.get_page()
for i in self.buttons:
if i in ('but_menu', 'but_request'):
continue
if self.buttons[i]:
self.buttons[i].set_sensitive(
bool(view and (i in view.handlers)))
@@ -1484,10 +1527,13 @@ class Main(object):
del page
self.notebook.set_current_page(next_page_id)
if not self.pages and self.menu_screen:
self.menu_screen.current_view.set_cursor()
return self.notebook.get_current_page() != -1
def _wid_get(self):
page_id = self.notebook.get_current_page()
def get_page(self, page_id=None):
if page_id is None:
page_id = self.notebook.get_current_page()
if page_id == -1:
return None
page_widget = self.notebook.get_nth_page(page_id)
@@ -1497,7 +1543,7 @@ class Main(object):
return None
def _sig_child_call(self, widget, button_name):
wid = self._wid_get()
wid = self.get_page()
if wid:
res = True
if button_name in wid.handlers:
@@ -1508,6 +1554,14 @@ class Main(object):
def _sig_page_changt(self, notebook, page, page_num):
self.last_page = self.current_page
self.current_page = self.notebook.get_current_page()
current_form = self.get_page(self.current_page)
parent = self.toolbar.get_parent()
if parent:
parent.remove(self.toolbar)
current_form.toolbar_box.add(self.toolbar)
current_form.toolbar_box.show_all()
self.sb_set()
def sig_db_new(self, widget):
@@ -1681,7 +1735,6 @@ class Main(object):
return
res_id = False
mode = None
view_type = params.get('view_type', 'form')
try:
view_ids = json.loads(params.get('views', 'false'))
limit = json.loads(params.get('limit', 'null'))
@@ -1700,9 +1753,9 @@ class Main(object):
mode = ['form', 'tree']
try:
Window.create(view_ids, model, res_id=res_id, domain=domain,
view_type=view_type, window=self.window,
context=context, mode=mode, name=name, limit=limit,
auto_refresh=auto_refresh, search_value=search_value)
window=self.window, context=context, mode=mode,
name=name, limit=limit, auto_refresh=auto_refresh,
search_value=search_value)
except Exception:
return
+1 -1
View File
@@ -15,7 +15,7 @@ class Attachment(object):
def run(self):
screen = Screen('ir.attachment', self.parent, domain=[
('resource', '=', self.resource),
], view_type=['tree', 'form'], context={
], mode=['tree', 'form'], context={
'resource': self.resource,
}, exclude_field='resource')
screen.search_filter()
+31 -2
View File
@@ -2,6 +2,7 @@
#this repository contains the full copyright notices and license terms.
"Board"
import gtk
import pango
from tryton.signal_event import SignalEvent
import tryton.rpc as rpc
from tryton.gui.window.view_board import ViewBoard
@@ -11,7 +12,7 @@ import tryton.common as common
class Board(SignalEvent):
'Board'
def __init__(self, window, view_id, context=None, name=False,
def __init__(self, model, window, view_id, context=None, name=False,
auto_refresh=False):
super(Board, self).__init__()
@@ -28,10 +29,38 @@ class Board(SignalEvent):
self.name = self.board.name
else:
self.name = name
self.model = ''
self.model = model
self.widget = gtk.VBox()
title = gtk.Label()
title.set_use_markup(True)
title.modify_font(pango.FontDescription("14"))
title.set_label('<b>' + self.name + '</b>')
title.set_padding(20, 4)
title.set_alignment(0.0, 0.5)
title.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse("#000000"))
title.show()
hbox = gtk.HBox()
hbox.pack_start(title, expand=True, fill=True)
hbox.show()
frame = gtk.Frame()
frame.set_shadow_type(gtk.SHADOW_ETCHED_IN)
frame.add(hbox)
frame.show()
eb = gtk.EventBox()
eb.add(frame)
eb.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse("#ffffff"))
eb.show()
self.widget.pack_start(eb, expand=False, fill=True, padding=3)
self.toolbar_box = gtk.HBox()
self.widget.pack_start(self.toolbar_box, False, True)
viewport = gtk.Viewport()
viewport.set_shadow_type(gtk.SHADOW_NONE)
viewport.add(self.board.widget_get())
+15 -9
View File
@@ -27,13 +27,13 @@ _ = gettext.gettext
class Form(SignalEvent):
"Form"
def __init__(self, model, window, res_id=False, domain=None, view_type=None,
def __init__(self, model, window, 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 view_type:
view_type = ['tree', 'form']
if not mode:
mode = ['tree', 'form']
if domain is None:
domain = []
if view_ids is None:
@@ -49,7 +49,7 @@ class Form(SignalEvent):
self.widget = gtk.VBox(spacing=3)
self.widget.show()
self.screen = Screen(self.model, self.window, view_type=view_type,
self.screen = Screen(self.model, self.window, mode=mode,
context=self.context, view_ids=view_ids, domain=domain,
limit=limit, readonly=bool(auto_refresh),
search_value=search_value)
@@ -110,6 +110,9 @@ class Form(SignalEvent):
self.widget.pack_start(eb, expand=False, fill=True, padding=3)
self.toolbar_box = gtk.HBox()
self.widget.pack_start(self.toolbar_box, False, True)
viewport = gtk.Viewport()
viewport.set_shadow_type(gtk.SHADOW_NONE)
viewport.add(self.screen.widget)
@@ -198,12 +201,15 @@ class Form(SignalEvent):
self.window.present()
def destroy(self):
if self.toolbar_box.get_children():
toolbar = self.toolbar_box.get_children()[0]
self.toolbar_box.remove(toolbar)
self.screen.signal_unconnect(self)
self.screen.destroy()
self.screen = None
self.widget = None
self.scrolledwindow.destroy()
self.scrolledwindow = None
#self.scrolledwindow.destroy()
#self.scrolledwindow = None
def sel_ids_get(self):
return self.screen.sel_ids_get()
@@ -361,7 +367,7 @@ class Form(SignalEvent):
def sig_reload(self, test_modified=True):
if not hasattr(self, 'screen'):
return False
if test_modified and self.screen.is_modified():
if test_modified and self.screen.modified():
res = sur_3b(_('This record has been modified\n' \
'do you want to save it ?'), self.window)
if res == 'ok':
@@ -441,7 +447,7 @@ class Form(SignalEvent):
def _record_message(self, screen, signal_data):
name = '_'
if signal_data[0] >= 0:
name = str(signal_data[0] + 1)
name = str(signal_data[0])
msg = name + ' / ' + str(signal_data[1])
if signal_data[1] < signal_data[2]:
msg += _(' of ') + str(signal_data[2])
@@ -455,7 +461,7 @@ class Form(SignalEvent):
self.signal('attachment-count', signal_data)
def modified_save(self, reload=True):
if self.screen.is_modified():
if self.screen.modified():
value = sur_3b(
_('This record has been modified\n' \
'do you want to save it ?'),
+5 -7
View File
@@ -37,10 +37,10 @@ class Preference(object):
args = ('model', 'res.user', 'get_preferences_fields_view',
rpc.CONTEXT)
try:
res = rpc.execute(*args)
view = rpc.execute(*args)
except Exception, exception:
res = common.process_exception(exception, parent, *args)
if not res:
view = common.process_exception(exception, parent, *args)
if not view:
self.win.destroy()
self.win = None
return
@@ -48,11 +48,9 @@ class Preference(object):
title = gtk.Label(_('Edit User Preferences'))
title.show()
self.win.vbox.pack_start(title, expand=False, fill=True)
arch = res['arch']
fields = res['fields']
self.screen = Screen('res.user', self.win, view_type=[])
self.screen = Screen('res.user', self.win, mode=[])
self.screen.new(default=False)
self.screen.add_view(arch, fields)
self.screen.add_view(view)
args = ('model', 'res.user', 'get_preferences', False, rpc.CONTEXT)
try:
-397
View File
@@ -1,397 +0,0 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
"Tree"
import gtk
import gettext
import tryton.common as common
from tryton.gui.window.view_tree import ViewTree, ViewTreeSC
import tryton.rpc as rpc
from tryton.config import CONFIG
from tryton.gui.window.win_export import WinExport
from window import Window
from tryton.action import Action
from tryton.signal_event import SignalEvent
_ = gettext.gettext
class Tree(SignalEvent):
"Tree page"
def __init__(self, model, window, res_id=False, view_id=False, domain=None,
context=None, name=False):
super(Tree, self).__init__()
if domain is None:
domain = {}
if context is None:
context = {}
ctx = {}
ctx.update(context)
ctx.update(rpc.CONTEXT)
if view_id:
try:
view_base = rpc.execute('model', 'ir.ui.view', 'read', view_id,
['model', 'type'], ctx)
except Exception, exception:
common.process_exception(exception, window)
raise
try:
view = rpc.execute('model', view_base['model'],
'fields_view_get', view_id, view_base['type'], ctx)
except Exception, exception:
common.process_exception(exception, window)
raise
else:
try:
view = rpc.execute('model', model, 'fields_view_get', False,
'tree', ctx)
except Exception, exception:
common.process_exception(exception, window)
raise
self.widget = gtk.VBox()
hpaned = gtk.HPaned()
hpaned.set_position(220)
self.toolbar_vpaned = gtk.VPaned()
self.toolbar_vpaned.set_position(400)
self.toolbar = gtk.Toolbar()
self.toolbar.set_orientation(gtk.ORIENTATION_VERTICAL)
self.toolbar.set_style(gtk.TOOLBAR_BOTH_HORIZ)
self.toolbar_vpaned.add1(self.toolbar)
self.toolbar_vpaned.add1(self.toolbar_vpaned)
scrolledwindow = gtk.ScrolledWindow()
scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC,
gtk.POLICY_AUTOMATIC)
viewport = gtk.Viewport()
viewport.set_shadow_type(gtk.SHADOW_ETCHED_IN)
vbox = gtk.VBox()
toolbar = gtk.Toolbar()
toolbar.set_style(gtk.TOOLBAR_ICONS)
toolitem = gtk.ToolItem()
self.toolitem_label = gtk.Label()
toolitem.add(self.toolitem_label)
toolbar.insert(toolitem, -1)
addbutton = gtk.ToolButton('tryton-list-add')
addbutton.connect('clicked', self.sc_add)
toolbar.insert(addbutton, -1)
removebutton = gtk.ToolButton('tryton-list-remove')
removebutton.connect('clicked', self.sc_del)
toolbar.insert(removebutton, -1)
vbox.pack_start(toolbar, expand=False)
self.treeview_sc = gtk.TreeView()
self.treeview_sc.set_reorderable(False)
self.treeview_sc.set_headers_visible(False)
vbox.pack_start(self.treeview_sc)
viewport.add(vbox)
scrolledwindow.add(viewport)
self.toolbar_vpaned.add2(scrolledwindow)
hpaned.add1(self.toolbar_vpaned)
viewport = gtk.Viewport()
viewport.set_shadow_type(gtk.SHADOW_ETCHED_IN)
self.scrolledwindow = gtk.ScrolledWindow()
self.scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC,
gtk.POLICY_AUTOMATIC)
viewport.add(self.scrolledwindow)
hpaned.add2(viewport)
self.widget.pack_start(hpaned)
self.widget.show_all()
self.model = view['model'] or model
self.domain2 = domain
if view.get('field_childs', False):
self.domain = []
else:
self.domain = domain
self.view = view
self.window = window
self.context = context
self.tree_res = ViewTree(view, [], self.window, True,
context=context)
self.tree_res.view.connect('row-activated', self.sig_activate)
self.tree_res.view.connect('key_press_event', self.sig_key_press)
self.tree_res.view.connect_after('test-expand-row',
self.sig_test_expand_row)
if not name:
self.name = self.tree_res.name
else:
self.name = name
if CONFIG['client.modepda']:
self.toolbar_vpaned.hide()
self.treeview_sc.connect('row-activated', self.sc_go)
self.tree_sc = ViewTreeSC(self.treeview_sc, self.model, self.window)
self.handlers = {
'but_new': self.sig_new,
'but_reload': self.sig_reload,
'but_switch': self.sig_edit,
'but_action': self.sig_action,
'but_print': self.sig_print,
'but_save_as': self.sig_save_as,
'but_close': self.sig_close,
}
self.scrolledwindow.add(self.tree_res.widget_get())
self.sig_reload()
self.tree_res.view.grab_focus()
if self.tree_res.view.get_model().get_iter_root():
self.tree_res.view.grab_focus()
selection = self.tree_res.view.get_selection()
selection.select_path((0))
def sig_reload(self, widget=None):
self.toolitem_label.set_text(_('Shortcuts'))
try:
ctx = {}
ctx.update(self.context)
ctx.update(rpc.CONTEXT)
args = ('model', self.model, 'search', self.domain2, 0, None,
None, ctx)
ids = rpc.execute(*args)
except Exception, exception:
ids = common.process_exception(exception, self.window, *args)
if not ids:
return
if self.tree_res.toolbar and not CONFIG['client.modepda']:
for child in self.toolbar.get_children():
self.toolbar.remove(child)
ctx = {}
ctx.update(rpc.CONTEXT)
try:
args = ('model', self.view['model'], 'read', ids,
['name', 'icon'], ctx)
results = rpc.execute(*args)
except Exception, exception:
results = common.process_exception(exception, self.window, *args)
if not results:
return
results.sort(lambda x, y: cmp(ids.index(x['id']),
ids.index(y['id'])))
radiotb = None
for res in results:
common.ICONFACTORY.register_icon(res['icon'])
radiotb = gtk.RadioToolButton(radiotb, res['icon'])
radiotb.set_label(res['name'])
radiotb.show_all()
radiotb.set_data('id', res['id'])
radiotb.connect('clicked', self.menu_main_clicked)
self.menu_main_clicked(radiotb, focus=False)
self.toolbar.insert(radiotb, -1)
radiotb.child.connect('key_press_event', self.menu_main_key_press)
else:
self.tree_res.ids = ids
self.tree_res.reload()
self.toolbar.hide()
self.toolbar_vpaned.set_position(-1)
self.tree_res.view.grab_focus()
if self.tree_res.view.get_model().get_iter_root():
self.tree_res.view.grab_focus()
selection = self.tree_res.view.get_selection()
selection.select_path((0))
self.tree_sc.update()
def menu_main_clicked(self, widget, focus=True):
if widget.get_active():
obj_id = widget.get_data('id')
args = ('model', self.model, 'read', obj_id,
[self.view['field_childs']], rpc.CONTEXT)
try:
ids = rpc.execute(*args)[self.view['field_childs']]
except Exception, exception:
res = common.process_exception(exception, self.window, *args)
if not res:
return False
ids = res[self.view['field_childs']]
if not ids:
return False
self.tree_res.ids = ids
self.tree_res.reload()
self.sig_action('tree_open', obj_id=obj_id, warning=False)
if focus:
if self.tree_res.view.get_model().get_iter_root():
self.tree_res.view.grab_focus()
selection = self.tree_res.view.get_selection()
selection.unselect_all()
selection.select_path((0))
self.tree_res.view.set_cursor((0))
return False
def menu_main_key_press(self, widget, event):
if event.keyval == gtk.keysyms.Right:
if self.tree_res.view.get_model().get_iter_root():
self.tree_res.view.grab_focus()
selection = self.tree_res.view.get_selection()
selection.unselect_all()
selection.select_path((0))
self.tree_res.view.set_cursor((0))
return True
def sig_print(self):
self.sig_action('form_print')
def sig_action(self, keyword='tree_action', obj_id=None, warning=True):
ids = self.ids_get()
if not obj_id and ids and len(ids):
obj_id = ids[0]
if obj_id:
ctx = self.context.copy()
if 'active_ids' in ctx:
del ctx['active_ids']
if 'active_id' in ctx:
del ctx['active_id']
return Action.exec_keyword(keyword, self.window, {
'model': self.model,
'id': obj_id,
'ids':ids,
}, context=ctx, warning=warning)
else:
common.message(_('No record selected!'), self.window)
return False
def sig_activate(self, widget, iter, path):
if not self.sig_action('tree_open', warning=False):
if self.tree_res.view.row_expanded(iter):
self.tree_res.view.collapse_row(iter)
else:
self.tree_res.view.expand_row(iter, False)
if self.model != 'ir.ui.menu':
self.sig_edit()
def sig_key_press(self, widget, event):
if event.keyval == gtk.keysyms.Left:
selection = self.tree_res.view.get_selection()
model, paths = selection.get_selected_rows()
if len(paths) == 1:
if not self.tree_res.view.row_expanded(paths[0]):
if len(paths[0]) > 1:
new_path = paths[0][:-1]
selection.select_path(new_path)
self.tree_res.view.collapse_row(new_path)
elif self.tree_res.toolbar:
for child in self.toolbar.get_children():
if child.get_active():
child.child.grab_focus()
break
for path in paths:
self.tree_res.view.collapse_row(path)
return True
elif event.keyval == gtk.keysyms.Right:
model, paths = self.tree_res.view.get_selection()\
.get_selected_rows()
for path in paths:
self.tree_res.view.expand_row(path, False)
return True
def sig_test_expand_row(self, widget, iter, path):
model = self.tree_res.view.get_model()
iter_children = model.iter_children(iter)
if iter_children and model.get(iter_children, 0)[0] in model.to_reload:
hostname = rpc._SOCK.hostname
port = rpc._SOCK.port
while True:
password = common.ask(_('Password:'), self.window,
visibility=False)
if password is None:
return True
res = rpc.login(rpc._USERNAME, password, hostname, port,
rpc._DATABASE)
if res == -1:
common.message(_('Connection error!\n' \
'Unable to connect to the server!'), self.window)
return True
if res < 0:
continue
return False
return False
def sig_new(self):
Window.create(None, self.model, domain=self.domain, window=self.window,
context=self.context, mode=['form', 'tree'])
def sig_edit(self):
obj_ids = self.ids_get()
if not obj_ids:
obj_ids = []
if self.tree_res.toolbar:
for child in self.toolbar.get_children():
if child.get_active():
obj_ids.append(child.get_data('id'))
mode = ['form', 'tree']
if len(obj_ids) > 1:
mode = ['tree', 'form']
Window.create(None, self.model, obj_ids, self.domain,
window=self.window, context=self.context,
mode=mode)
def sc_del(self, widget):
obj_id = self.tree_sc.sel_id_get()
if obj_id is not None:
sc_id = int(self.tree_sc.value_get(2))
try:
rpc.execute('model', 'ir.ui.view_sc', 'delete', sc_id,
rpc.CONTEXT)
except Exception, exception:
common.process_exception(exception, self.window)
self.tree_sc.update()
def sc_add(self, widget):
ids = self.ids_get()
if len(ids):
try:
res = rpc.execute('model', self.model, 'read', ids,
['rec_name'], rpc.CONTEXT)
for obj in res:
obj_id = obj['id']
name = obj['rec_name']
user = rpc._USER
rpc.execute('model', 'ir.ui.view_sc', 'create', {
'resource': self.model,
'user_id': user,
'res_id': obj_id,
'name': name,
}, rpc.CONTEXT)
except Exception, exception:
common.process_exception(exception, self.window)
self.tree_sc.update()
def sc_go(self, widget=None, *args):
obj_id = self.tree_sc.sel_id_get()
if obj_id is not None:
self.sig_action(keyword='tree_open', obj_id=obj_id)
def ids_get(self):
res = self.tree_res.sel_ids_get()
return res
def id_get(self):
res = self.tree_res.sel_id_get()
return res
def sig_save_as(self, widget=None):
win = WinExport(self.model, self.ids_get(), self.window,
context=self.context)
win.run()
def sig_close(self):
return True
+9 -29
View File
@@ -8,7 +8,6 @@ import time
import datetime
from tryton.gui.window.win_search import WinSearch
from tryton.action import Action as Action2
from tryton.gui.window.view_tree.view_tree import ViewTree
import tryton.common as common
from tryton.pyson import PYSONDecoder
import gettext
@@ -46,6 +45,7 @@ class Action(SignalEvent):
self.action.setdefault('pyson_domain', '[]')
self.context = {'active_id': False, 'active_ids': []}
self.context.update(rpc.CONTEXT)
self.context.update(PYSONDecoder(self.context).decode(
self.action.get('pyson_context', '{}')))
@@ -73,7 +73,7 @@ class Action(SignalEvent):
tooltips = common.Tooltips()
if self.action['view_type'] == 'form':
if self.action['res_model']:
but_search = gtk.Button()
tooltips.set_tip(but_search, _('Search'))
but_search.connect('clicked', self._sig_search)
@@ -96,7 +96,7 @@ class Action(SignalEvent):
hbox.pack_start(but_open, expand=False, fill=False)
if self.action['view_type'] == 'form':
if self.action['res_model']:
hbox.pack_start(gtk.VSeparator(), expand=False, fill=True)
but_previous = gtk.Button()
tooltips.set_tip(but_previous, _('Previous'))
@@ -140,11 +140,13 @@ class Action(SignalEvent):
alignment.set_padding(0, 2, 2, 2)
vbox.pack_start(alignment, expand=True, fill=True)
hbox.set_focus_chain([])
self.widget.show_all()
if self.action['view_type'] == 'form':
if self.action['res_model']:
self.screen = Screen(self.action['res_model'],
self._window, view_type=self.action['view_mode'],
self._window, mode=self.action['view_mode'],
context=self.context, view_ids=view_ids,
domain=self.domain, readonly=True, alternate_view=True)
alignment.add(self.screen.screen_container.alternate_viewport)
@@ -152,29 +154,6 @@ class Action(SignalEvent):
self.screen.signal_connect(self, 'record-message', self._sig_label)
self.screen.signal_connect(self, 'record-message',
self._active_changed)
elif self.action['view_type'] == 'tree':
ctx = {}
ctx.update(rpc.CONTEXT)
ctx.update(self.context)
try:
view_base = rpc.execute('model', 'ir.ui.view', 'read',
view_ids[0], ['model', 'type'], ctx)
except Exception, exception:
common.process_exception(exception, self._window)
raise
try:
view = rpc.execute('model', view_base['model'],
'fields_view_get', view_ids[0], view_base['type'], ctx)
except Exception, exception:
common.process_exception(exception, self._window)
raise
self.tree = ViewTree(view, [], self._window, True,
context=ctx)
alignment.add(self.tree.widget_get())
name = self.tree.name
self.tree.view.connect('key_press_event', self.sig_key_press)
self.tree.signal_connect(self, 'select-changed',
self._active_changed)
if attrs.get('string'):
self.title.set_text(attrs['string'])
@@ -219,7 +198,7 @@ class Action(SignalEvent):
def _sig_label(self, screen, signal_data):
name = '_'
if signal_data[0] >= 0:
name = str(signal_data[0] + 1)
name = str(signal_data[0])
line = '(%s/%s)' % (name, signal_data[1])
self.label.set_text(line)
@@ -268,6 +247,7 @@ class Action(SignalEvent):
def update_domain(self, actions):
domain_ctx = self.context.copy()
domain_ctx['context'] = domain_ctx
for action in actions:
if action.active:
domain_ctx['_active_%s' % action.act_id] = action.active
+146 -74
View File
@@ -27,8 +27,7 @@ class Field(object):
class CharField(object):
def __init__(self, parent, attrs):
self.parent = parent
def __init__(self, attrs):
self.attrs = attrs
self.name = attrs['name']
self.internal = False
@@ -65,8 +64,9 @@ class CharField(object):
return screen_domain, screen_domain
def context_get(self, record, check_load=True, eval_context=True):
context = {}
context.update(self.parent.context)
context = record.context_get().copy()
if record.parent:
context.update(record.parent.context_get())
if eval_context:
context.update(record.expr_eval(self.attrs.get('context', {}),
check_load=check_load))
@@ -114,8 +114,9 @@ class CharField(object):
def set(self, record, value, modified=False):
record.value[self.name] = value
if modified:
record.modified = True
record.modified_fields.setdefault(self.name)
record.signal('record-modified')
record.signal('record-changed')
return True
def get(self, record, check_load=True, readonly=True, modified=False):
@@ -130,16 +131,16 @@ class CharField(object):
def set_client(self, record, value, force_change=False):
internal = record.value.get(self.name, False)
prev_modified = record.modified
prev_modified_fields = record.modified_fields.copy()
self.set(record, value)
if (internal or False) != (record.value.get(self.name, False) or False):
record.modified = True
record.modified_fields.setdefault(self.name)
record.signal('record-modified')
try:
self.sig_changed(record)
except Exception:
record.value[self.name] = internal
record.modified = prev_modified
record.modified_fields = prev_modified_fields
return
record.validate(softvalidation=True)
record.signal('record-changed')
@@ -185,9 +186,6 @@ class CharField(object):
def get_timestamp(self, record):
return {}
def destroy(self):
self.parent = None
class SelectionField(CharField):
@@ -232,19 +230,20 @@ class FloatField(CharField):
def set_client(self, record, value, force_change=False):
internal = record.value.get(self.name)
prev_modified = record.modified
prev_modified_fields = record.modified_fields.copy()
self.set(record, value)
digits = record.expr_eval(self.attrs.get('digits', (16, 2)))
if abs(float(internal or 0.0) - float(record.value[self.name] or 0.0)) \
>= (10.0**(-int(digits[1]))):
if not self.get_state_attrs(record).get('readonly', False):
record.modified = True
record.modified_fields.setdefault(self.name)
record.signal('record-modified')
try:
self.sig_changed(record)
except Exception:
record.value[self.name] = internal
record.modified = prev_modified
record.modified_fields = prev_modified_fields
return
record.validate(softvalidation=True)
record.signal('record-changed')
@@ -254,20 +253,20 @@ class NumericField(CharField):
def set_client(self, record, value, force_change=False):
value = Decimal(str(value))
internal = record.value.get(self.name)
prev_modified = record.modified
prev_modified_fields = record.modified_fields.copy()
self.set(record, value)
digits = record.expr_eval(self.attrs.get('digits', (16, 2)))
if abs((internal or Decimal('0.0')) - \
(record.value[self.name] or Decimal('0.0'))) \
>= Decimal(str(10.0**(-int(digits[1])))):
if not self.get_state_attrs(record).get('readonly', False):
record.modified = True
record.modified_fields.setdefault(self.name)
record.signal('record-modified')
try:
self.sig_changed(record)
except Exception:
record.value[self.name] = internal
record.modified = prev_modified
record.prev_modified_fields = prev_modified_fields
return
record.validate(softvalidation=True)
record.signal('record-changed')
@@ -286,16 +285,16 @@ class BooleanField(CharField):
def set_client(self, record, value, force_change=False):
value = bool(value)
internal = bool(record.value.get(self.name, False))
prev_modified = record.modified
prev_modified_fields = record.modified_fields.copy()
self.set(record, value)
if internal != bool(record.value.get(self.name, False)):
record.modified = True
record.modified_fields.setdefault(self.name)
record.signal('record-modified')
try:
self.sig_changed(record)
except Exception:
record.value[self.name] = internal
record.modified = prev_modified
record.modified_fields = prev_modified_fields
return
record.validate(softvalidation=True)
record.signal('record-changed')
@@ -313,25 +312,41 @@ class M2OField(CharField):
'''
def get(self, record, check_load=True, readonly=True, modified=False):
if record.value.get(self.name):
if isinstance(record.value[self.name], (int, basestring, long)):
self.set(record, record.value[self.name])
if isinstance(record.value[self.name], (int, basestring, long)):
return record.value[self.name]
return record.value[self.name][0] or False
value = record.value.get(self.name)
if record.parent_name == self.name:
value = record.parent.id if record.parent else False
if value:
if isinstance(value, (int, basestring, long)):
self.set(record, value)
value = record.value.get(self.name)
if isinstance(value, (int, basestring, long)):
return value
return value[0] or False
return False
def get_client(self, record):
if record.value.get(self.name):
if isinstance(record.value[self.name], (int, basestring, long)):
self.set(record, record.value[self.name])
if isinstance(record.value[self.name], (int, basestring, long)):
return record.value[self.name]
return record.value[self.name][1]
value = record.value.get(self.name)
if record.parent_name == self.name:
value = record.parent.id if record.parent else False
if value:
if isinstance(value, (int, basestring, long)):
self.set(record, value)
value = record.value.get(self.name)
if isinstance(value, (int, basestring, long)):
return value
return value[1]
return False
def set(self, record, value, modified=False):
if value and isinstance(value, (int, long)):
if record.parent_name == self.name:
if record.parent:
if 'rec_name' in record.parent.value:
value = (record.parent.id, record.parent.value['rec_name'])
else:
value = record.parent.id
else:
value = False
if value and isinstance(value, (int, long)) and value > 0:
args = ('model', self.attrs['relation'], 'read', value,
['rec_name'], rpc.CONTEXT)
try:
@@ -342,7 +357,8 @@ class M2OField(CharField):
if not result:
return
value = value, result['rec_name']
if value and len(value) != 2:
if value and (isinstance(value, (int, long))
or len(value) != 2):
value = (False, '')
record.value[self.name + '.rec_name'] = ''
else:
@@ -351,22 +367,28 @@ class M2OField(CharField):
else:
record.value[self.name + '.rec_name'] = ''
record.value[self.name] = value or (False, '')
if record.parent_name == self.name:
if record.parent:
if 'rec_name' not in record.parent.value:
record.parent.value['rec_name'] = \
record.value[self.name + '.rec_name']
if modified:
record.modified = True
record.modified_fields.setdefault(self.name)
record.signal('record-modified')
record.signal('record-changed')
def set_client(self, record, value, force_change=False):
internal = record.value.get(self.name) or (False, '')
prev_modified = record.modified
prev_modified_fields = record.modified_fields.copy()
self.set(record, value)
if (internal[0] or False) != (record.value[self.name][0] or False):
record.modified = True
record.modified_fields.setdefault(self.name)
record.signal('record-modified')
try:
self.sig_changed(record)
except Exception:
record.value[self.name] = internal
record.modified = prev_modified
record.modified_fields = prev_modified_fields
return
record.validate(softvalidation=True)
record.signal('record-changed')
@@ -395,6 +417,12 @@ class M2OField(CharField):
screen_domain, attr_domain = self.domains_get(record)
return localize_domain(inverse_leaf(screen_domain), self.name) + attr_domain
def get_state_attrs(self, record):
result = super(M2OField, self).get_state_attrs(record)
if record.parent_name == self.name:
result = result.copy()
result['readonly'] = True
return result
class O2OField(M2OField):
pass
@@ -405,27 +433,42 @@ class O2MField(CharField):
internal = Group of the related objects
'''
def __init__(self, parent, attrs):
super(O2MField, self).__init__(parent, attrs)
def __init__(self, attrs):
super(O2MField, self).__init__(attrs)
self.context = {}
def _group_changed(self, group, record):
record.parent.modified = True
if not record.parent:
return
record.parent.modified_fields.setdefault(self.name)
record.parent.signal('record-modified')
self.sig_changed(record.parent)
record.parent.validate(softvalidation=True)
record.parent.signal('record-changed')
record.parent.group.signal('group-changed', record.parent)
def _group_list_changed(self, group, signal):
group.parent.group.signal('group-list-changed', signal)
def _group_cleared(self, group, signal):
group.parent.signal('group-cleared')
def _set_default_value(self, record):
if record.value.get(self.name) is not None:
return
from group import Group
parent_name = self.attrs.get('relation_field', '')
group = Group(self.attrs['relation'], {}, record.window,
parent=record,
parent_name=self.attrs.get('relation_field', ''),
parent_name=parent_name,
child_name=self.name,
context=self.context,
parent_datetime_field=self.attrs.get('datetime_field'))
if record.model_name == self.attrs['relation']:
group.fields = record.group.fields
group.signal_connect(group, 'group-changed', self._group_changed)
group.signal_connect(group, 'group-list-changed', self._group_list_changed)
group.signal_connect(group, 'group-cleared', self._group_cleared)
record.value[self.name] = group
def get_client(self, record):
@@ -440,7 +483,7 @@ class O2MField(CharField):
if record2.id > 0:
values = record2.get(check_load=check_load,
get_readonly=readonly, get_modifiedonly=modified)
if record2.is_modified() and values:
if record2.modified and values:
result.append(('write', record2.id, values))
result[0][1].append(record2.id)
else:
@@ -479,18 +522,28 @@ class O2MField(CharField):
def set(self, record, value, modified=False):
from group import Group
group = record.value.get(self.name)
fields = {}
if group is not None:
group.signal_unconnect(group)
fields = group.fields
elif record.model_name == self.attrs['relation']:
fields = record.group.fields
parent_name = self.attrs.get('relation_field', '')
group = Group(self.attrs['relation'], {}, record.window,
parent=record, parent_name=self.attrs.get('relation_field', ''),
parent=record, parent_name=parent_name,
child_name=self.name,
context=self.context,
parent_datetime_field=self.attrs.get('datetime_field'))
group.fields = fields
record.value[self.name] = group
record.value[self.name].load(value, display=False)
group.load(value, display=False)
group.signal_connect(group, 'group-changed', self._group_changed)
group.signal_connect(group, 'group-list-changed', self._group_list_changed)
group.signal_connect(group, 'group-cleared', self._group_cleared)
if modified:
record.modified = True
record.modified_fields.setdefault(self.name)
record.signal('record-modified')
record.signal('record-changed')
def set_client(self, record, value, force_change=False):
pass
@@ -502,31 +555,38 @@ class O2MField(CharField):
if value and len(value) and isinstance(value[0], (int, long)):
return self.set(record, value, modified=modified)
# value is a list of dict
group = record.value.get(self.name)
fields = {}
if group is not None:
group.signal_unconnect(group)
fields = group.fields
elif record.model_name == self.attrs['relation']:
fields = record.group.fields
# value is a list of dict
fields_dict = {}
if value and len(value):
context = self.context_get(record)
field_names = []
for val in value:
for fieldname in val.keys():
if fieldname not in field_names:
if (fieldname not in field_names
and fieldname not in fields):
field_names.append(fieldname)
args = ('model', self.attrs['relation'], 'fields_get',
field_names, context)
try:
fields = rpc.execute(*args)
except Exception, exception:
fields = common.process_exception(exception, record.window,
*args)
if not fields:
return False
if field_names:
args = ('model', self.attrs['relation'], 'fields_get',
field_names, context)
try:
fields_to_load = rpc.execute(*args)
except Exception, exception:
fields_dict = common.process_exception(exception,
record.window, *args)
if not fields_dict:
return False
group = record.value.get(self.name)
if group is not None:
group.signal_unconnect(group)
group = Group(
self.attrs['relation'], fields, record.window, parent=record,
parent_name=self.attrs.get('relation_field', ''),
parent_name = self.attrs.get('relation_field', '')
group = Group(self.attrs['relation'], fields_dict, record.window,
parent=record, parent_name=parent_name, child_name=self.name,
context=self.context,
parent_datetime_field=self.attrs.get('datetime_field'))
if record.value.get(self.name):
@@ -535,12 +595,14 @@ class O2MField(CharField):
group.record_deleted.extend(record.value[self.name].record_deleted)
group.record_removed.extend(record.value[self.name].record_removed)
record.value[self.name] = group
group.fields = fields
for vals in (value or []):
new_record = record.value[self.name].new(default=False)
new_record.set_default(vals, modified=modified)
group.add(new_record)
group.signal_connect(group,
'group-changed', self._group_changed)
group.signal_connect(group, 'group-changed', self._group_changed)
group.signal_connect(group, 'group-list-changed', self._group_list_changed)
group.signal_connect(group, 'group-cleared', self._group_cleared)
return True
def set_on_change(self, record, value):
@@ -607,7 +669,7 @@ class O2MField(CharField):
if not record2.loaded:
continue
if not record2.validate():
if not record2.is_modified():
if not record2.modified:
record.value[self.name].remove(record2)
else:
res = False
@@ -643,10 +705,16 @@ class M2MField(O2MField):
def set(self, record, value, modified=False):
from group import Group
group = record.value.get(self.name)
fields = {}
if group is not None:
group.signal_unconnect(group)
fields = group.fields
elif record.model_name == self.attrs['relation']:
fields = record.group.fields
parent_name = self.attrs.get('relation_field', '')
group = Group(self.attrs['relation'], {}, record.window,
parent=record, parent_name=self.attrs.get('relation_field', ''),
parent=record, parent_name=parent_name,
child_name=self.name,
context=self.context,
parent_datetime_field=self.attrs.get('datetime_field'))
if record.value.get(self.name):
@@ -654,11 +722,15 @@ class M2MField(O2MField):
group.record_deleted.extend(record.value[self.name].record_deleted)
group.record_removed.extend(record.value[self.name].record_removed)
record.value[self.name] = group
record.value[self.name].load(value, display=False)
group.fields = fields
group.load(value, display=False)
group.signal_connect(group, 'group-changed', self._group_changed)
group.signal_connect(group, 'group-list-changed', self._group_list_changed)
group.signal_connect(group, 'group-cleared', self._group_cleared)
if modified:
record.modified = True
record.modified_fields.setdefault(self.name)
record.signal('record-modified')
record.signal('record-changed')
class ReferenceField(CharField):
@@ -680,16 +752,16 @@ class ReferenceField(CharField):
def set_client(self, record, value, force_change=False):
internal = record.value.get(self.name)
prev_modified = record.modified
prev_modified_fields = record.modified_fields.copy()
self.set(record, value)
if (internal or False) != (record.value[self.name] or False):
record.modified = True
record.modified_fields.setdefault(self.name)
record.signal('record-modified')
try:
self.sig_changed(record)
except Exception:
record.value[self.name] = internal
record.modified = prev_modified
record.modified_fields = prev_modified_fields
return
record.validate(softvalidation=True)
record.signal('record-changed')
@@ -706,7 +778,7 @@ class ReferenceField(CharField):
ref_id = int(ref_id)
if not ref_id:
ref_str = ''
if not ref_str and ref_id:
if not ref_str and ref_id > 0:
args = ('model', ref_model, 'read', ref_id,
['rec_name'], rpc.CONTEXT)
try:
@@ -731,8 +803,8 @@ class ReferenceField(CharField):
if self.name + '.rec_name' in record.value:
del record.value[self.name + '.rec_name']
if modified:
record.modified = True
record.modified_fields.setdefault(self.name)
record.signal('record-modified')
TYPES = {
'char': CharField,
+65 -41
View File
@@ -1,5 +1,6 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
import copy
import tryton.rpc as rpc
from record import Record
from field import Field, O2MField
@@ -10,8 +11,8 @@ import tryton.common as common
class Group(SignalEvent, list):
def __init__(self, model_name, fields, window, ids=None, parent=None,
parent_name='', context=None, domain=None, readonly=False,
parent_datetime_field=None):
parent_name='', child_name='', context=None, domain=None,
readonly=False, parent_datetime_field=None):
super(Group, self).__init__()
if domain is None:
domain = []
@@ -20,6 +21,7 @@ class Group(SignalEvent, list):
self.__window = window
self.parent = parent
self.parent_name = parent_name
self.child_name = child_name
self.parent_datetime_field = parent_datetime_field
self._context = context or {}
self.model_name = model_name
@@ -33,6 +35,7 @@ class Group(SignalEvent, list):
if self._context.get('_datetime'):
self.readonly = True
self.__id2record = {}
self.__field_childs = None
def __get_window(self):
return self.__window
@@ -45,6 +48,7 @@ class Group(SignalEvent, list):
window = property(__get_window, __set_window)
def insert(self, pos, record):
assert record.group is self
if pos >= 1:
self.__getitem__(pos - 1).next[id(self)] = record
if pos < self.__len__():
@@ -54,16 +58,17 @@ class Group(SignalEvent, list):
super(Group, self).insert(pos, record)
self.__id2record[record.id] = record
if not self.lock_signal:
self.signal('group-list-changed', ('record-added', pos))
self.signal('group-list-changed', ('record-added', record))
def append(self, record):
assert record.group is self
if self.__len__() >= 1:
self.__getitem__(self.__len__() - 1).next[id(self)] = record
record.next[id(self)] = None
super(Group, self).append(record)
self.__id2record[record.id] = record
if not self.lock_signal:
self.signal('group-list-changed', ('record-added', -1))
self.signal('group-list-changed', ('record-added', record))
def _remove(self, record):
idx = self.index(record)
@@ -73,24 +78,23 @@ class Group(SignalEvent, list):
self.__getitem__(idx + 1)
else:
self.__getitem__(idx - 1).next[id(self)] = None
if not self.lock_signal:
self.signal('group-list-changed', ('record-removed', record))
super(Group, self).remove(record)
del self.__id2record[record.id]
if not self.lock_signal:
self.signal('group-list-changed', ('record-removed', idx))
def clear(self):
if not self.lock_signal:
for record in self:
self.signal('group-list-changed', ('record-removed', 0))
del self[:]
for record in self[:]:
self.signal('group-list-changed', ('record-removed', record))
self.pop(0)
if not self.lock_signal:
self.signal('group-list-changed', ('group-cleared',))
self.__id2record = {}
self.record_removed, self.record_deleted = [], []
def move(self, record, pos):
self.lock_signal = True
if self.__len__() > pos:
if self.__len__() > pos >= 0:
idx = self.index(record)
self._remove(record)
if pos > idx:
@@ -99,7 +103,6 @@ class Group(SignalEvent, list):
else:
self._remove(record)
self.append(record)
self.lock_signal = False
def __setitem__(self, i, value):
super(Group, self).__setitem__(i, value)
@@ -113,7 +116,7 @@ class Group(SignalEvent, list):
for name, attr in fields.iteritems():
field = Field(attr['type'])
attr['name'] = name
self.fields[name] = field(self, attr)
self.fields[name] = field(attr)
if isinstance(self.fields[name], O2MField) \
and '_datetime' in self._context:
self.fields[name].context.update({
@@ -121,17 +124,26 @@ class Group(SignalEvent, list):
})
def save(self):
saved = []
for record in self:
saved = record.save()
self.writen(saved)
saved.append(record.save())
return saved
def writen(self, ids):
@property
def root_group(self):
root = self
parent = self.parent
while parent:
root = parent.group
return root
def written(self, ids):
if isinstance(ids, (int, long)):
ids = [ids]
ids = [x for x in self.on_write_ids(ids) or [] if x not in ids]
if not ids:
return
self.reload(ids)
self.root_group.reload(ids)
return ids
def reload(self, ids):
@@ -154,7 +166,7 @@ class Group(SignalEvent, list):
res += res2
return list({}.fromkeys(res))
def load(self, ids, display=True, modified=False):
def load(self, ids, display=True, modified=False, id2record=None):
if not ids:
return True
@@ -166,7 +178,7 @@ class Group(SignalEvent, list):
if self.get(id):
continue
new_record = Record(self.model_name, id, self.window,
parent=self.parent, parent_name=self.parent_name, group=self)
group=self)
self.append(new_record)
new_records.append(new_record)
new_record.signal_connect(self, 'record-changed',
@@ -206,26 +218,21 @@ class Group(SignalEvent, list):
context = property(_get_context)
def add(self, record, position=-1, modified=True):
if not record.group is self:
fields = {}
for i in record.group.fields:
fields[record.group.fields[i].attrs['name']] = \
record.group.fields[i].attrs
self.add_fields(fields)
if record.group is not self:
record.signal_unconnect(record.group)
record.group = self
record.window = self.window
record.signal_connect(self, 'record-changed', self._record_changed)
record.signal_connect(self, 'record-modified', self._record_modified)
if position == -1:
self.append(record)
else:
self.insert(position, record)
self.current_idx = position
record.parent = self.parent
record.parent_name = self.parent_name
record.window = self.window
if modified:
record.modified = True
record.signal_connect(self, 'record-changed', self._record_changed)
record.signal_connect(self, 'record-modified', self._record_modified)
record.modified_fields.setdefault('id')
record.signal('record-modified')
self.signal('group-changed', record)
return record
def set_sequence(self, field='sequence'):
@@ -238,9 +245,9 @@ class Group(SignalEvent, list):
else:
index = record[field].get(record)
def new(self, default=True, domain=None, context=None, signal=True):
record = Record(self.model_name, None, self.window, group=self,
parent=self.parent, parent_name=self.parent_name)
def new(self, default=True, domain=None, context=None, signal=True,
obj_id=None):
record = Record(self.model_name, obj_id, self.window, group=self)
record.signal_connect(self, 'record-changed', self._record_changed)
record.signal_connect(self, 'record-modified', self._record_modified)
if default:
@@ -260,7 +267,8 @@ class Group(SignalEvent, list):
if signal:
record.signal('record-changed', record.parent)
def remove(self, record, remove=False, modified=True, signal=True):
def remove(self, record, remove=False, modified=True, signal=True,
force_remove=False):
idx = self.index(record)
if self[idx].id > 0:
if remove:
@@ -272,10 +280,12 @@ class Group(SignalEvent, list):
self.record_removed.remove(self[idx])
self.record_deleted.append(self[idx])
if record.parent:
record.parent.modified = True
record.parent.modified_fields.setdefault('id')
record.parent.signal('record-modified')
if modified:
record.modified = True
if not record.parent or self[idx].id <= 0:
record.modified_fields.setdefault('id')
record.signal('record-modified')
if not record.parent or self[idx].id <= 0 or force_remove:
self._remove(self[idx])
if len(self):
@@ -360,11 +370,25 @@ class Group(SignalEvent, list):
super(Group, self).destroy()
self.__window = None
self.parent = None
for field in self.fields.itervalues():
field.destroy()
self.fields = {}
self.record_deleted, self.record_removed = [], []
self.__id2record = None
for record in self:
record.destroy()
self[:] = []
def get_by_path(self, path):
'return record by path'
group = self
record = None
for child_name, id_ in path:
record = group.get(id_)
if not record:
return None
if not child_name:
continue
record[child_name]
group = record.value.get(child_name)
if not isinstance(group, Group):
return None
return record
+105 -53
View File
@@ -14,22 +14,18 @@ class Record(SignalEvent):
id = -1
def __init__(self, model_name, obj_id, window, group=None, parent=None,
parent_name=''):
def __init__(self, model_name, obj_id, window, group=None):
super(Record, self).__init__()
self.window = window
self.__window = window
self.model_name = model_name
self.id = obj_id or Record.id
if self.id < 0:
Record.id -= 1
self._loaded = set()
self.parent = parent
self.parent_name = parent_name
self.group = group
if group is not None:
assert model_name == group.model_name
self.state_attrs = {}
self.__modified = False
self.modified_fields = {}
self._timestamp = None
self.attachment_count = -1
@@ -37,7 +33,7 @@ class Record(SignalEvent):
self.value = {}
self.autocompletion = {}
def __getitem__(self, name):
def __getitem__(self, name, raise_exception=False):
if name not in self._loaded and self.id > 0:
ids = [self.id]
if name == '*':
@@ -68,6 +64,7 @@ class Record(SignalEvent):
if field.attrs.get('loading', 'eager') == 'eager']
else:
fields = self.group.fields.keys()
fields = [fname for fname in fields if fname not in self._loaded]
fields.extend(('%s.rec_name' % fname for fname in fields[:]
if self.group.fields[fname].attrs['type']
in ('many2one', 'one2one', 'reference')))
@@ -78,6 +75,8 @@ class Record(SignalEvent):
try:
values = rpc.execute(*args)
except Exception, exception:
if raise_exception:
raise
values = common.process_exception(exception, self.window, *args)
if not values:
values = [{'id': x} for x in ids]
@@ -95,17 +94,67 @@ class Record(SignalEvent):
return self.group.fields.get(name, False)
def __repr__(self):
return '<Record %s@%s>' % (self.id, self.model_name)
return '<Record %s@%s at %s>' % (self.id, self.model_name, id(self))
def get_modified(self):
return self.__modified
@property
def modified(self):
return bool(self.modified_fields)
@property
def parent(self):
return self.group.parent
@property
def parent_name(self):
return self.group.parent_name
def set_modified(self, value):
self.__modified = value
if value:
self.signal('record-modified')
modified = property(get_modified, set_modified)
def children_group(self, field_name, check_load=True):
if not field_name:
return []
if check_load:
self._check_load([field_name])
group = self.value.get(field_name)
if group is False or group is None:
return []
if id(group.fields) != id(self.group.fields):
self.group.fields.update(group.fields)
group.fields = self.group.fields
group.on_write = self.group.on_write
group.readonly = self.group.readonly
if group.window != self.window:
group.window = self.window
return group
def get_path(self, group):
path = []
i = self
child_name = ''
while i:
path.append((child_name, i.id))
if i.group is group:
break
child_name = i.group.child_name
i = i.parent
path.reverse()
return tuple(path)
def _get_window(self):
return self.__window
def _set_window(self, window):
self.__window = window
for fieldname, value in self.value.iteritems():
if fieldname not in self.group.fields:
continue
if isinstance(self.group.fields[fieldname], field.O2MField):
value.window = window
window = property(_get_window, _set_window)
def get_removed(self):
if self.group is not None:
@@ -126,9 +175,6 @@ class Record(SignalEvent):
readonly = property(get_readonly)
def is_modified(self):
return self.modified
def fields_get(self):
return self.group.fields
@@ -187,40 +233,48 @@ class Record(SignalEvent):
return result
def save(self, force_reload=True):
if self.id < 0:
value = self.get(get_readonly=True)
args = ('model', self.model_name, 'create', value,
self.context_get())
try:
res = rpc.execute(*args)
except Exception, exception:
res = common.process_exception(exception, self.window, *args)
if not res:
return False
old_id = self.id
self.id = res
self.group.id_changed(old_id)
else:
if not self.is_modified():
return self.id
self._check_load()
value = self.get(get_readonly=True, get_modifiedonly=True)
context = self.context_get()
context = context.copy()
context['_timestamp'] = self.get_timestamp()
args = ('model', self.model_name, 'write', [self.id], value,
context)
try:
if not rpc.execute(*args):
return False
except Exception, exception:
if not common.process_exception(exception, self.window, *args):
return False
self._loaded.clear()
if force_reload:
self.reload()
if self.group:
self.group.writen(self.id)
if self.id < 0 or self.modified:
if self.id < 0:
value = self.get(get_readonly=True)
args = ('model', self.model_name, 'create', value,
self.context_get())
try:
res = rpc.execute(*args)
except Exception, exception:
res = common.process_exception(exception, self.window,
*args)
if not res:
return False
old_id = self.id
self.id = res
self.group.id_changed(old_id)
elif self.modified:
self._check_load()
value = self.get(get_readonly=True, get_modifiedonly=True,
check_load=False)
if value:
context = self.context_get()
context = context.copy()
context['_timestamp'] = self.get_timestamp()
args = ('model', self.model_name, 'write', [self.id],
value, context)
try:
if not rpc.execute(*args):
return False
except Exception, exception:
res = common.process_exception(exception, self.window,
*args)
if not res:
return False
self._loaded.clear()
self.modified_fields = {}
if force_reload:
self.reload()
if self.group:
self.group.written(self.id)
if self.parent:
self.parent.modified_fields.pop(self.group.child_name, None)
self.parent.save(force_reload=force_reload)
return self.id
def default_get(self, domain=None, context=None):
@@ -233,6 +287,8 @@ class Record(SignalEvent):
vals = common.process_exception(exception, self.window, *args)
if not vals:
return
if self.parent_name in self.group.fields and self.parent:
vals[self.parent_name] = self.parent.id
self.set_default(vals)
for fieldname, fieldinfo in self.group.fields.iteritems():
if not fieldinfo.attrs.get('autocomplete'):
@@ -331,9 +387,6 @@ class Record(SignalEvent):
for fieldname, value in later.iteritems():
self.group.fields[fieldname].set(self, value, modified=modified)
self._loaded.add(fieldname)
self.modified = modified
if not self.modified:
self.modified_fields = {}
if signal:
self.signal('record-changed')
@@ -496,7 +549,6 @@ class Record(SignalEvent):
def destroy(self):
super(Record, self).destroy()
self.window = None
self.parent = None
self.group = None
self.value = None
self.next = None
+195 -140
View File
@@ -1,6 +1,9 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
"Screen"
import gobject
import copy
import xml.dom.minidom
import tryton.rpc as rpc
from tryton.gui.window.view_form.model.group import Group
from tryton.gui.window.view_form.view.screen_container import ScreenContainer
@@ -9,22 +12,19 @@ from tryton.signal_event import SignalEvent
from tryton.common import node_attributes
from tryton.config import CONFIG
import tryton.common as common
import gobject
import copy
import xml.dom.minidom
class Screen(SignalEvent):
"Screen"
def __init__(self, model_name, window, view_ids=None, view_type=None,
def __init__(self, model_name, window, view_ids=None, mode=None,
context=None, views_preload=None, domain=None, row_activate=None,
limit=None, readonly=False, exclude_field=None, sort=None,
search_value=None, alternate_view=False):
if view_ids is None:
view_ids = []
if view_type is None:
view_type = ['tree', 'form']
if mode is None:
mode = ['tree', 'form']
if context is None:
context = {}
if views_preload is None:
@@ -34,10 +34,10 @@ class Screen(SignalEvent):
super(Screen, self).__init__()
self.readonly = readonly
self.search_count = 0
if not row_activate:
# TODO change for a function that switch to form view
self.row_activate = self.switch_view
self.row_activate = self.default_row_activate
else:
self.row_activate = row_activate
self.domain = domain
@@ -65,12 +65,12 @@ class Screen(SignalEvent):
self.sort = sort
self.view_to_load = []
if view_type:
self.view_to_load = view_type[1:]
if mode:
self.view_to_load = mode[1:]
view_id = False
if view_ids:
view_id = view_ids.pop(0)
view = self.add_view_id(view_id, view_type[0])
view = self.add_view_id(view_id, mode[0])
self.screen_container.set(view.widget)
self.display()
@@ -90,10 +90,9 @@ class Screen(SignalEvent):
'tree', ctx)
except Exception:
return
self.filter_widget = Form(self.fields_view_tree['arch'],
self.fields_view_tree['fields'], self.model_name,
self.window, self.domain, (self, self.search_filter),
self.context)
self.filter_widget = Form(self.fields_view_tree,
self.model_name, self.window, self.domain,
(self, self.search_filter), self.context)
self.screen_container.add_filter(self.filter_widget.widget,
self.search_filter, self.search_clear,
self.search_prev, self.search_next)
@@ -194,9 +193,9 @@ class Screen(SignalEvent):
group = property(__get_group, __set_group)
def new_group(self, readonly=False):
def new_group(self):
self.group = Group(self.model_name, {}, self.window, domain=self.domain,
context=self.context, readonly=readonly)
context=self.context, readonly=self.readonly)
def _group_cleared(self, group, signal):
for view in self.views:
@@ -224,10 +223,16 @@ class Screen(SignalEvent):
except Exception:
offset = 0
try:
pos = self.group.index(record)
except Exception:
pos = -1
self.signal('record-message', (pos + offset, len(self.group) + offset,
pos = self.group.index(record) + offset + 1
except ValueError:
pos = []
i = record
while i:
pos.append(i.group.index(i) + 1)
i = i.parent
pos.reverse()
pos = tuple(pos)
self.signal('record-message', (pos or 0, len(self.group) + offset,
self.search_count, record and record.id))
attachment_count = 0
if record and record.attachment_count > 0:
@@ -268,9 +273,25 @@ class Screen(SignalEvent):
self.screen_container = None
self.widget = None
def default_row_activate(self):
from tryton.action import Action
if (self.current_view.view_type == 'tree' and
self.current_view.widget_tree.keyword_open):
return Action.exec_keyword('tree_open', self.window, {
'model': self.model_name,
'id': self.id_get(),
'ids': self.ids_get(),
}, context=self.context.copy(), warning=False)
else:
self.switch_view(view_type='form')
return True
def switch_view(self, view_type=None, default=True, context=None):
if self.modified():
return
self.current_view.set_value()
if self.current_record and self.current_record not in self.group:
if (self.current_record and
self.current_record not in self.current_record.group):
self.current_record = None
fields = self.current_view.get_fields()
if self.current_record and not self.current_record.validate(fields):
@@ -283,7 +304,8 @@ class Screen(SignalEvent):
self.load_view_to_load()
self.__current_view = len(self.views) - 1
else:
self.__current_view = (self.__current_view + 1) % len(self.views)
self.__current_view = ((self.__current_view + 1)
% len(self.views))
if not view_type:
break
elif self.current_view.view_type == view_type:
@@ -306,10 +328,7 @@ class Screen(SignalEvent):
def add_view_id(self, view_id, view_type, display=False, context=None):
if view_type in self.views_preload:
return self.add_view(self.views_preload[view_type]['arch'],
self.views_preload[view_type]['fields'], display,
toolbar=self.views_preload[view_type].get('toolbar', False),
context=context)
view = self.views_preload[view_type]
else:
ctx = {}
ctx.update(rpc.CONTEXT)
@@ -324,17 +343,19 @@ class Screen(SignalEvent):
view = common.process_exception(exception, self.window, *args)
if not view:
return
return self.add_view(view['arch'], view['fields'], display,
toolbar=view.get('toolbar', False), context=context)
return self.add_view(view, display, toolbar=view.get('toolbar', False),
context=context)
def add_view(self, arch, fields, display=False, toolbar=None, context=None):
def add_view(self, view, display=False, toolbar=None, context=None):
if toolbar is None:
toolbar = {}
arch = view['arch']
fields = view['fields']
xml_dom = xml.dom.minidom.parseString(arch)
for node in xml_dom.childNodes:
if node.localName == 'tree':
self.fields_view_tree = {'arch': arch, 'fields': fields}
self.fields_view_tree = view
break
if node.localName == 'tree':
@@ -345,22 +366,14 @@ class Screen(SignalEvent):
if field not in self.group.fields:
fields[field]['loading'] = loading
children_field = view.get('field_childs')
from tryton.gui.window.view_form.view.widget_parse import WidgetParse
if self.current_record and (self.current_record not in self.group):
self.group.append(self.current_record)
self.group.add_fields(fields, context=context)
if self.exclude_field:
if self.exclude_field in self.group.fields:
field = self.group.fields[self.exclude_field]
field.attrs['states'] = {'invisible': True}
field.attrs['readonly'] = True
field.attrs['invisible'] = True
field.attrs['tree_invisible'] = True
field.attrs['exclude_field'] = True
parser = WidgetParse(parent=self.parent, window=self.window)
view = parser.parse(self, xml_dom, self.group.fields, toolbar=toolbar)
view = parser.parse(self, xml_dom, self.group.fields, toolbar=toolbar,
children_field=children_field)
self.views.append(view)
@@ -406,8 +419,12 @@ class Screen(SignalEvent):
ctx.update(rpc.CONTEXT)
ctx.update(self.context)
ctx.update(context)
record = self.group.new(default, self.domain, ctx)
self.group.add(record, self.new_model_position())
if self.current_record:
group = self.current_record.group
else:
group = self.group
record = group.new(default, self.domain, ctx)
group.add(record, self.new_model_position())
self.current_record = record
fields = None
if self.current_view:
@@ -440,31 +457,27 @@ class Screen(SignalEvent):
def save_current(self):
if not self.current_record:
return False
if self.current_view == 'tree' and len(self.group):
self.current_record = self.group[0]
else:
return True
self.current_view.set_value()
obj_id = False
fields = self.current_view.get_fields()
if self.current_record.validate(fields):
path = self.current_record.get_path(self.group)
if self.current_view.view_type == 'tree':
self.group.save()
obj_id = self.current_record.id
elif self.current_record.validate(fields):
obj_id = self.current_record.save(force_reload=True)
else:
self.current_view.set_cursor()
self.current_view.display()
return False
if self.current_view.view_type == 'tree':
for record in self.group:
if record.is_modified():
if record.validate(fields):
obj_id = record.save(force_reload=True)
else:
self.current_view.set_cursor()
self.current_record = record
self.current_view.set_cursor()
self.display()
return False
self.current_view.set_cursor()
if path:
path = path[:-1] + ((path[-1][0], obj_id),)
self.current_record = self.group.get_by_path(path)
self.display()
if self.current_record not in self.group:
self.group.add(self.current_record, modified=False)
self.request_set()
return obj_id
@@ -484,22 +497,22 @@ class Screen(SignalEvent):
includeid=includeid, check_load=check_load,
get_modifiedonly=get_modifiedonly)
def is_modified(self):
def modified(self):
self.current_view.set_value()
res = False
if self.current_view.view_type != 'tree':
res = self.current_record and self.current_record.is_modified()
res = self.current_record and self.current_record.modified
else:
for record in self.group:
if record.is_modified():
if record.modified:
res = True
return res
def reload(self, writen=False):
def reload(self, written=False):
ids = self.sel_ids_get()
self.group.reload(ids)
if writen:
self.group.writen(ids)
if written:
self.group.written(ids)
if self.parent:
self.parent.reload()
self.display()
@@ -532,35 +545,43 @@ class Screen(SignalEvent):
if not res:
return False
self.current_view.set_cursor()
if self.current_record in self.group:
idx = self.group.index(self.current_record)
self.group.remove(self.current_record, remove=remove)
if self.group:
idx = min(idx, len(self.group) - 1)
self.current_record = self.group[idx]
else:
self.current_record = None
record = self.current_record
idx = record.group.index(record)
record.group.remove(record, remove=remove)
if delete:
if (record.parent and
record.parent.model_name == record.model_name):
record.parent.save()
if record.group:
idx = min(idx, len(record.group) - 1)
self.current_record = record.group[idx]
elif (record.parent and
record.parent.model_name == record.model_name):
self.current_record = record.parent
else:
self.current_record = None
if reload_ids:
self.group.reload(reload_ids)
self.group.root_group.reload(reload_ids)
self.display()
res = True
if self.current_view.view_type == 'tree':
ids = self.current_view.sel_ids_get()
if delete and ids:
records = self.current_view.selected_records()
if delete and records:
context = {}
context.update(rpc.CONTEXT)
context.update(self.context)
context['_timestamp'] = {}
for record_id in ids:
record = self.group.get(record_id)
for record in records:
context['_timestamp'].update(record.get_timestamp())
reload_ids = self.group.on_write_ids(ids)
reload_ids = self.group.on_write_ids([x.id for x in records])
if reload_ids:
for record_id in ids:
if record_id in reload_ids:
reload_ids.remove(record_id)
args = ('model', self.model_name, 'delete', ids,
context)
for record in records:
if record.id in reload_ids:
reload_ids.remove(record.id)
args = ('model', self.model_name, 'delete',
[x.id for x in records], context)
try:
res = rpc.execute(*args)
except Exception, exception:
@@ -568,24 +589,34 @@ class Screen(SignalEvent):
*args)
if not res:
return False
sel_records = self.current_view.selected_records()
if not sel_records:
if not records:
return True
idx = self.group.index(sel_records[0])
for record in sel_records:
path = self.current_view.store.on_get_path(records[0])
for record in records:
# set current model to None to prevent __select_changed
# to save the previous_model as it can be already deleted.
self.current_record = None
self.group.remove(record, remove=remove, signal=False)
record.group.remove(record, remove=remove, signal=False)
# send record-changed only once
record.signal('record-changed')
if self.group:
idx = min(idx, len(self.group) - 1)
self.current_record = self.group[idx]
if delete:
for record in records:
if record.parent:
record.parent.save()
if path[-1] > 0:
path = path[:-1] + (path[-1] - 1,)
else:
self.current_record = None
path = path[:-1]
if path:
iter_ = self.current_view.store.get_iter(path)
self.current_record = self.current_view.store.get_value(iter_, 0)
elif len(self.group):
self.current_record = self.group[0]
if reload_ids:
self.group.reload(reload_ids)
self.group.root_group.reload(reload_ids)
self.current_view.set_cursor()
self.display()
res = True
@@ -624,54 +655,78 @@ class Screen(SignalEvent):
self.current_view.set_cursor(reset_view=False)
def display_next(self):
self.current_view.set_value()
self.current_view.set_cursor(reset_view=False)
if self.current_record in self.group:
idx = self.group.index(self.current_record)
inc = 1
if self.current_view.view_type == 'tree':
start, end = self.current_view.widget_tree.get_visible_range()
inc += end[0] - start[0]
if inc >= 4 and (end[0] + 1) < len(self.group):
inc -= 3
vadjustment = self.current_view.widget_tree.get_vadjustment()
vadjustment.value = vadjustment.value + vadjustment.page_increment
idx = idx + inc
if idx >= len(self.group):
idx = len(self.group) - 1
self.current_record = self.group[idx]
view = self.current_view
view.set_value()
view.set_cursor(reset_view=False)
if view.view_type == 'tree':
start, end = view.widget_tree.get_visible_range()
vadjustment = view.widget_tree.get_vadjustment()
vadjustment.value = vadjustment.value + vadjustment.page_increment
store = view.store
iter_ = store.get_iter(end)
self.current_record = store.get_value(iter_, 0)
elif self.current_record.group:
group = self.current_record.group
record = self.current_record
while group:
children = record.children_group(view.children_field)
if children:
record = children[0]
break
idx = group.index(record) + 1
if idx < len(group):
record = group[idx]
break
parent = record.parent
if not parent:
break
next = parent.next.get(id(parent.group))
while not next:
parent = parent.parent
if not parent:
break
next = parent.next.get(id(parent.group))
if not next:
break
record = next
break
self.current_record = record
else:
self.current_record = len(self.group) \
and self.group[0]
self.current_view.set_cursor(reset_view=False)
self.current_view.display()
self.current_record = len(self.group) and self.group[0]
view.set_cursor(reset_view=False)
view.display()
def display_prev(self):
self.current_view.set_value()
self.current_view.set_cursor(reset_view=False)
if self.current_record in self.group:
inc = 1
if self.current_view.view_type == 'tree':
range = self.current_view.widget_tree.get_visible_range()
if range:
start, end = range
inc += end[0] - start[0]
if inc >= 4 and start[0] > 0:
inc -= 3
vadjustment = \
self.current_view.widget_tree.get_vadjustment()
if vadjustment.value:
vadjustment.value = vadjustment.value - \
vadjustment.page_increment
idx = self.group.index(self.current_record) - inc
if idx < 0:
idx = 0
self.current_record = self.group[idx]
view = self.current_view
view.set_value()
view.set_cursor(reset_view=False)
if view.view_type == 'tree':
start, end = view.widget_tree.get_visible_range()
vadjustment = view.widget_tree.get_vadjustment()
vadjustment.value = vadjustment.value - vadjustment.page_increment
store = view.store
iter_ = store.get_iter(start)
self.current_record = store.get_value(iter_, 0)
elif self.current_record.group:
group = self.current_record.group
record = self.current_record
idx = group.index(record) - 1
if idx >= 0:
record = group[idx]
children = True
while children:
children = record.children_group(view.children_field)
if children:
record = children[-1]
else:
parent = record.parent
if parent:
record = parent
self.current_record = record
else:
self.current_record = len(self.group) \
and self.group[-1]
self.current_view.set_cursor(reset_view=False)
self.current_view.display()
self.current_record = len(self.group) and self.group[-1]
view.set_cursor(reset_view=False)
view.display()
def sel_ids_get(self):
return self.current_view.sel_ids_get()
+4 -3
View File
@@ -16,9 +16,10 @@ _ = gettext.gettext
class ViewForm(ParserView):
def __init__(self, window, screen, widget, children=None,
buttons=None, toolbar=None, notebooks=None, cursor_widget=''):
buttons=None, toolbar=None, notebooks=None, cursor_widget='',
children_field=None):
super(ViewForm, self).__init__(window, screen, widget, children,
buttons, toolbar, notebooks, cursor_widget)
buttons, toolbar, notebooks, cursor_widget, children_field)
self.view_type = 'form'
for button in self.buttons:
@@ -157,7 +158,7 @@ class ViewForm(ParserView):
value = Action._exec_action(act, self.window, data, {})
if atype in ('print', 'action'):
if self.screen:
self.screen.reload(writen=True)
self.screen.reload(written=True)
return value
def __getitem__(self, name):
@@ -60,7 +60,7 @@ class Many2Many(WidgetInterface):
hbox.set_focus_chain([self.wid_text])
self.screen = Screen(attrs['relation'], self.window,
view_type=['tree'], views_preload=attrs.get('views', {}),
mode=['tree'], views_preload=attrs.get('views', {}),
row_activate=self._on_activate)
if not isinstance(self.screen.window, gtk.Dialog):
@@ -147,7 +147,7 @@ class Many2One(WidgetInterface):
domain = self.field.domain_get(self.record)
context = self.field.context_get(self.record)
return Screen(self.attrs['relation'], self.window, domain=domain,
context=context, view_type=['form'],
context=context, mode=['form'],
views_preload=self.attrs.get('views', {}))
def sig_new(self, *args):
@@ -154,7 +154,7 @@ class One2Many(WidgetInterface):
self.widget.pack_start(frame, expand=False, fill=True)
self.screen = Screen(attrs['relation'], self.window,
view_type=attrs.get('mode', 'tree,form').split(','),
mode=attrs.get('mode', 'tree,form').split(','),
views_preload=attrs.get('views', {}),
row_activate=self._on_activate,
exclude_field=attrs.get('relation_field', None))
@@ -324,17 +324,17 @@ class One2Many(WidgetInterface):
def _sig_label(self, screen, signal_data):
name = '_'
if signal_data[0] >= 0:
name = str(signal_data[0] + 1)
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] + 1 < signal_data[1]:
if signal_data[0] < signal_data[1]:
self.but_next.set_sensitive(True)
else:
self.but_next.set_sensitive(False)
if signal_data[0] > 0:
if signal_data[0] > 1:
self.but_pre.set_sensitive(True)
else:
self.but_pre.set_sensitive(False)
@@ -381,7 +381,7 @@ class One2Many(WidgetInterface):
def set_value(self, record, field):
self.screen.current_view.set_value()
if self.screen.is_modified():
record.modified = True
if self.screen.modified(): # TODO check if required
record.modified_fields.setdefault(field.name)
record.signal('record-modified')
return True
@@ -83,7 +83,7 @@ class Button(object):
}, self.form.window, context=ctx)
else:
raise Exception('Unallowed button type')
self.form.screen.reload(writen=True)
self.form.screen.reload(written=True)
else:
self.form.screen.display()
@@ -292,9 +292,10 @@ class _container(object):
class ParserForm(ParserInterface):
def __init__(self, window, parent=None, attrs=None, screen=None):
def __init__(self, window, parent=None, attrs=None, screen=None,
children_field=None):
super(ParserForm, self).__init__(window, parent=parent, attrs=attrs,
screen=screen)
screen=screen, children_field=children_field)
self.widget_id = 0
def parse(self, model_name, root_node, fields, notebook=None, paned=None,
@@ -322,8 +323,7 @@ class ParserForm(ParserInterface):
attrs = common.node_attributes(node)
if not cursor_widget:
if attrs.get('name') and fields.get(attrs['name']) \
and not fields[attrs['name']].attrs.get('exclude_field',
False):
and attrs['name'] != self.screen.exclude_field:
cursor_widget = attrs.get('name')
if node.localName == 'image':
common.ICONFACTORY.register_icon(attrs['name'])
@@ -359,6 +359,9 @@ class ParserForm(ParserInterface):
elif node.localName == 'label':
text = attrs.get('string', '')
if 'name' in attrs and attrs['name'] in fields:
if attrs['name'] == self.screen.exclude_field:
container.empty_add(int(attrs.get('colspan', 1)))
continue
for attr_name in ('states', 'invisible'):
if attr_name not in attrs \
and attr_name in fields[attrs['name']].attrs:
@@ -480,6 +483,8 @@ class ParserForm(ParserInterface):
gtk.gdk.PIXBUF_ROTATE_NONE)
text = attrs.get('string', '')
if 'name' in attrs and attrs['name'] in fields:
if attrs['name'] == self.screen.exclude_field:
continue
for attr_name in ('states', 'invisible'):
if attr_name in fields[attrs['name']].attrs:
attrs[attr_name] = \
@@ -532,6 +537,9 @@ class ParserForm(ParserInterface):
elif node.localName == 'field':
name = str(attrs['name'])
if name == self.screen.exclude_field:
container.empty_add(int(attrs.get('colspan', 1)))
continue
if name not in fields:
container.empty_add(int(attrs.get('colspan', 1)))
log = logging.getLogger('view')
@@ -593,6 +601,9 @@ class ParserForm(ParserInterface):
button_list += buttons
text = ''
if 'name' in attrs and attrs['name'] in fields:
if attrs['name'] == self.screen.exclude_field:
container.empty_add(int(attrs.get('colspan', 1)))
continue
for attr_name in ('states', 'invisible'):
if attr_name in fields[attrs['name']].attrs:
attrs[attr_name] = fields[attrs['name']
@@ -153,7 +153,7 @@ class Reference(WidgetInterface):
return False
if model and obj_id:
if not leave:
screen = Screen(model, self.window, view_type=['form'])
screen = Screen(model, self.window, mode=['form'])
screen.load([obj_id])
win = WinForm(screen, self.window)
while win.run():
@@ -207,7 +207,7 @@ class Reference(WidgetInterface):
model = self.get_model()
if not model:
return
screen = Screen(model, self.window, view_type=['form'])
screen = Screen(model, self.window, mode=['form'])
win = WinForm(screen, self.window, new=True)
while win.run():
if screen.save_current():
+3 -2
View File
@@ -6,9 +6,10 @@ from interface import ParserView
class ViewGraph(ParserView):
def __init__(self, window, screen, widget, children=None,
buttons=None, toolbar=None, notebooks=None, cursor_widget=None):
buttons=None, toolbar=None, notebooks=None, cursor_widget=None,
children_field=None):
super(ViewGraph, self).__init__(window, screen, widget, children,
buttons, toolbar, notebooks, cursor_widget)
buttons, toolbar, notebooks, cursor_widget, children_field)
self.view_type = 'graph'
self.widgets = children
@@ -5,19 +5,22 @@
class ParserInterface(object):
def __init__(self, window, parent=None, attrs=None, screen=None):
def __init__(self, window, parent=None, attrs=None, screen=None,
children_field=None):
self.window = window
self.parent = parent
self.attrs = attrs
self.title = None
self.buttons = {}
self.screen = screen
self.children_field = children_field
class ParserView(object):
def __init__(self, window, screen, widget, children=None, buttons=None,
toolbar=None, notebooks=None, cursor_widget=None):
toolbar=None, notebooks=None, cursor_widget=None,
children_field=None):
self.window = window
self.screen = screen
self.widget = widget
@@ -30,6 +33,7 @@ class ParserView(object):
notebooks = []
self.notebooks = notebooks
self.cursor_widget = cursor_widget
self.children_field = children_field
def get_fields(self):
return self.children.keys()
+253 -80
View File
@@ -2,6 +2,11 @@
#this repository contains the full copyright notices and license terms.
import gobject
import gtk
import sys
if sys.version_info < (2, 6):
import simplejson as json
else:
import json
import tryton.rpc as rpc
import locale
from interface import ParserView
@@ -20,25 +25,43 @@ _ = gettext.gettext
class AdaptModelGroup(gtk.GenericTreeModel):
def __init__(self, group):
def __init__(self, group, children_field=None):
super(AdaptModelGroup, self).__init__()
self.group = group
self.last_sort = None
self.sort_asc = True
self.set_property('leak_references', False)
self.children_field = children_field
self.__removed = None # XXX dirty hack to allow update of has_child
def added(self, modellist, position):
if modellist is self.group:
model = self.group[position]
self.row_inserted(self.on_get_path(model),
self.get_iter(self.on_get_path(model)))
def added(self, group, record):
if group is self.group:
path = self.on_get_path(record)
iter_ = self.get_iter(path)
self.row_inserted(path, iter_)
if record.children_group(self.children_field):
self.row_has_child_toggled(path, iter_)
if (record.parent and
record.group is not self.group):
path = self.on_get_path(record.parent)
iter_ = self.get_iter(path)
self.row_has_child_toggled(path, iter_)
def cancel(self):
pass
def removed(self, lst, position):
self.row_deleted(position)
self.invalidate_iters()
def removed(self, group, record):
if group is self.group:
path = self.on_get_path(record)
self.row_deleted(path)
if (record.parent and
record.group != self.group and
len(record.children_group(self.children_field)) <= 1):
path = self.on_get_path(record.parent)
iter_ = self.get_iter(path)
self.__removed = record # XXX check for thread
self.row_has_child_toggled(path, iter_)
self.__removed = None
def append(self, model):
self.group.add(model)
@@ -46,14 +69,45 @@ class AdaptModelGroup(gtk.GenericTreeModel):
def prepend(self, model):
self.group.add(model, 0)
def remove(self, iter):
idx = self.get_path(iter)[0]
self.group.remove(self.group[idx])
def remove(self, iter_):
record = self.get_value(iter_, 0)
record.group.remove(record)
self.invalidate_iters()
def move(self, path, position):
idx = path[0]
self.group.move(self.group[idx], position)
def __move(self, record, path, offset=0):
iter_ = self.get_iter(path)
record_pos = self.get_value(iter_, 0)
group = record_pos.group
pos = group.index(record_pos) + offset
if group is not record.group:
record.group.remove(record, remove=True, force_remove=True)
group.add(record)
record.modified_fields.setdefault(record.parent_name or 'id')
group.move(record, pos)
def move_before(self, record, path):
self.__move(record, path)
def move_after(self, record, path):
self.__move(record, path, 1)
def move_into(self, record, path):
iter_ = self.get_iter(path)
parent = self.get_value(iter_, 0)
group = parent.children_group(self.children_field, check_load=True)
if group is not record.group:
record.group.remove(record, remove=True, force_remove=True)
group.add(record)
record.modified_fields.setdefault(record.parent_name or 'id')
group.move(record, 0)
def move_end(self, record):
group = self.group
if group != record.group:
record.group.remove(record, remove=True)
group.add(record)
record.modified_fields.setdefault(record.parent_name or 'id')
group.move(record, -1)
def sort(self, ids):
ids2pos = {}
@@ -87,67 +141,96 @@ class AdaptModelGroup(gtk.GenericTreeModel):
return len(self.group)
def on_get_flags(self):
return gtk.TREE_MODEL_LIST_ONLY
if not self.children_field:
return gtk.TREE_MODEL_LIST_ONLY
return 0
def on_get_n_columns(self):
# XXX
return 1
def on_get_column_type(self, index):
# XXX
return gobject.TYPE_PYOBJECT
def on_get_path(self, iter):
if iter in self.group:
return self.group.index(iter)
def on_get_path(self, iter_):
if isinstance(iter_, tuple):
return tuple(x[0] for x in iter_)
else:
return 0
path = []
i = iter_
while i:
path.append(i.group.index(i))
if i.group is self.group:
break
i = i.parent
path.reverse()
return tuple(path)
def on_get_tree_path(self, iter):
return self.on_get_path(iter)
def on_get_iter(self, path):
if isinstance(path, tuple):
path = path[0]
if self.group is not None:
if path < len(self.group):
return self.group[path]
else:
group = self.group
record = None
for i in path:
if group is None or i >= len(group):
return None
else:
record = group[i]
if not self.children_field:
break
group = record.children_group(self.children_field, check_load=True)
return record
def on_get_value(self, record, column):
return record
def on_iter_next(self, record):
if not record:
return None
return record.next.get(id(record.group))
def on_get_value(self, node, column):
assert column == 0
return node
def on_iter_has_child(self, record):
if not self.children_field:
return False
children = record.children_group(self.children_field)
length = len(children)
if self.__removed and self.__removed in children:
length -= 1
return bool(length)
def on_iter_next(self, node):
try:
return node.next[id(self.group)]
except IndexError:
return None
def on_iter_has_child(self, node):
return False
def on_iter_children(self, node):
def on_iter_children(self, record):
if self.children_field and record.children_group(self.children_field):
return record.children_group(self.children_field)[0]
return None
def on_iter_n_children(self, node):
if node is None:
def on_iter_n_children(self, record):
if record is None or not self.children_field:
return len(self.group)
return 0
return len(record.children_group(self.children_field))
def on_iter_nth_child(self, node, nth):
if node is None and self.group is not None:
return self.on_get_iter(0)
def on_iter_nth_child(self, record, nth):
if record is None or not self.children_field:
if nth < len(self.group):
return self.group[nth]
return None
if nth < len(record.children_group(self.children_field)):
return record.children_group(self.children_field)[nth]
return None
def on_iter_parent(self, node):
return None
def on_iter_parent(self, record):
if record is None:
return None
return record.parent
class ViewList(ParserView):
def __init__(self, window, screen, widget, children=None, buttons=None,
toolbar=None, notebooks=None, cursor_widget=None):
toolbar=None, notebooks=None, cursor_widget=None,
children_field=None):
super(ViewList, self).__init__(window, screen, widget, children,
buttons, toolbar, notebooks, cursor_widget)
buttons, toolbar, notebooks, cursor_widget, children_field)
self.store = None
self.view_type = 'tree'
@@ -250,10 +333,22 @@ class ViewList(ParserView):
selection.set_mode(gtk.SELECTION_MULTIPLE)
selection.connect('changed', self.__select_changed)
dnd = False
if self.children_field:
children_field = self.widget_tree.cells.get(self.children_field)
if children_field:
parent_name = children_field.attrs.get('relation_field')
dnd = parent_name in self.widget_tree.cells
elif self.widget_tree.sequence:
dnd = True
# Disable DnD on mac until it is fully supported
if self.widget_tree.sequence \
and not (os.name == 'mac' \
or (hasattr(os, 'uname') and os.uname()[0] == 'Darwin')):
if os.name == 'mac':
dnd = False
elif hasattr(os, 'uname') and os.uname()[0] == 'Darwin':
dnd = False
if screen.readonly:
dnd = False
if dnd:
self.widget_tree.enable_model_drag_source(gtk.gdk.BUTTON1_MASK,
[('MY_TREE_MODEL_ROW', gtk.TARGET_SAME_WIDGET, 0),],
gtk.gdk.ACTION_MOVE)
@@ -270,6 +365,8 @@ class ViewList(ParserView):
self.widget_tree.connect('drag-data-delete', self.drag_data_delete)
self.widget_tree.connect('key_press_event', self.on_keypress)
self.widget_tree.connect('test-expand-row', self.test_expand_row)
self.widget_tree.set_expander_column(self.widget_tree.get_column(0))
def get_fields(self):
return [col.name for col in self.widget_tree.get_columns() if col.name]
@@ -298,7 +395,7 @@ class ViewList(ParserView):
}
value = Action._exec_action(act, self.window, data, {})
if self.screen:
self.screen.reload(writen=True)
self.screen.reload(written=True)
return value
@@ -307,16 +404,59 @@ class ViewList(ParserView):
self.on_copy()
return False
if event.keyval in (gtk.keysyms.Down, gtk.keysyms.Up):
path, column = self.widget_tree.get_cursor()
path, column = widget.get_cursor()
if not path:
return False
store = self.widget_tree.get_model()
model = widget.get_model()
if event.keyval == gtk.keysyms.Down:
if path[0] == len(store) - 1:
return True
test = True
for i in xrange(len(path)):
iter_ = model.get_iter(path[0:i+1])
if model.iter_next(iter_):
test = False
if test:
iter_ = model.get_iter(path)
if (model.iter_has_child(iter_)
and widget.row_expanded(path)):
test = False
return test
elif event.keyval == gtk.keysyms.Up:
if path[0] == 0:
if path == (0,):
return True
if event.keyval in (gtk.keysyms.Left, gtk.keysyms.Right):
selection = widget.get_selection()
model, paths = selection.get_selected_rows()
if event.keyval == gtk.keysyms.Left:
if len(paths) == 1:
path, = paths
if not widget.row_expanded(path):
path = path[:-1]
if path:
selection.select_path(path)
widget.collapse_row(path)
for path in paths:
widget.collapse_row(path)
elif event.keyval == gtk.keysyms.Right:
for path in paths:
widget.expand_row(path, False)
def test_expand_row(self, widget, iter_, path):
model = widget.get_model()
iter_ = model.iter_children(iter_)
if not iter_:
return False
fields = [col.name for col in self.widget_tree.get_columns()
if col.name]
while iter_:
record = model.get_value(iter_, 0)
if not record.get_loaded(fields):
try:
for field in fields:
record.__getitem__(field, True)
except Exception, exception:
return True
iter_ = model.iter_next(iter_)
return False
def on_copy(self):
clipboard = self.widget_tree.get_clipboard(gtk.gdk.SELECTION_CLIPBOARD)
@@ -371,8 +511,9 @@ class ViewList(ParserView):
def drag_data_get(self, treeview, context, selection, target_id,
etime):
treeview.emit_stop_by_name('drag-data-get')
def _func_sel_get(store, path, iter, data):
data.append(path)
def _func_sel_get(store, path, iter_, data):
value = store.get_value(iter_, 0)
data.append(json.dumps(value.get_path(store.group)))
data = []
treeselection = treeview.get_selection()
treeselection.selected_foreach(_func_sel_get, data)
@@ -392,20 +533,39 @@ class ViewList(ParserView):
return
if not selection.data:
return
model = treeview.get_model()
data = common.safe_eval(selection.data)
store = treeview.get_model()
try:
data = json.loads(selection.data)
except ValueError:
return
record = store.group.get_by_path(data)
record_path = store.on_get_path(record)
drop_info = treeview.get_dest_row_at_pos(x, y)
def check_recursion(from_, to):
if not from_ or not to:
return True
length = min(len(from_), len(to))
if len(from_) < len(to) and from_[:length] == to[:length]:
return False
return True
if drop_info:
path, position = drop_info
idx = path[0]
if position in (gtk.TREE_VIEW_DROP_BEFORE,
gtk.TREE_VIEW_DROP_INTO_OR_BEFORE):
model.move(data, idx)
else:
model.move(data, idx + 1)
check_path = path
if position in (gtk.TREE_VIEW_DROP_BEFORE, gtk.TREE_VIEW_DROP_AFTER):
check_path = path[:-1]
if not check_recursion(record_path, check_path):
return
if position == gtk.TREE_VIEW_DROP_BEFORE:
store.move_before(record, path)
elif position == gtk.TREE_VIEW_DROP_AFTER:
store.move_after(record, path)
elif self.children_field:
store.move_into(record, path)
else:
store.move_end(record)
context.drop_finish(False, etime)
if treeview.sequence:
self.screen.group.set_sequence(field=treeview.sequence)
record.group.set_sequence(field=treeview.sequence)
def drag_data_delete(self, treeview, context):
treeview.emit_stop_by_name('drag-data-delete')
@@ -543,21 +703,27 @@ class ViewList(ParserView):
def __sig_switch(self, treeview, path, column):
if column._type == 'button':
return
self.screen.row_activate()
if not self.screen.row_activate():
if treeview.row_expanded(path):
treeview.collapse_row(path)
else:
treeview.expand_row(path, False)
def __select_changed(self, tree_sel):
previous_record = self.screen.current_record
if tree_sel.get_mode() == gtk.SELECTION_SINGLE:
model, iter = tree_sel.get_selected()
if iter:
path = model.get_path(iter)[0]
self.screen.current_record = model.group[path]
model, iter_ = tree_sel.get_selected()
if model and iter_:
record = model.get_value(iter_, 0)
self.screen.current_record = record
elif tree_sel.get_mode() == gtk.SELECTION_MULTIPLE:
model, paths = tree_sel.get_selected_rows()
if paths:
self.screen.current_record = model.group[paths[0][0]]
if model and paths:
iter_ = model.get_iter(paths[0])
record = model.get_value(iter_, 0)
self.screen.current_record = record
if hasattr(self.widget_tree, 'editable') \
and self.widget_tree.editable \
@@ -587,7 +753,8 @@ class ViewList(ParserView):
or (not self.widget_tree.get_model()) \
or self.screen.group != \
self.widget_tree.get_model().group:
self.store = AdaptModelGroup(self.screen.group)
self.store = AdaptModelGroup(self.screen.group,
self.children_field)
self.widget_tree.set_model(self.store)
self.reload = False
if not self.screen.current_record:
@@ -647,6 +814,8 @@ class ViewList(ParserView):
self.widget_tree.grab_focus()
if self.screen.current_record:
path = self.store.on_get_path(self.screen.current_record)
if self.store.get_flags() & gtk.TREE_MODEL_LIST_ONLY:
path = (path[0],)
focus_column = None
for column in self.widget_tree.get_columns():
renderers = column.get_cell_renderers()
@@ -656,13 +825,16 @@ class ViewList(ParserView):
if isinstance(renderer, CellRendererToggle):
editable = renderer.get_property('activatable')
elif isinstance(renderer,
(gtk.CellRendererProgress, CellRendererButton)):
(gtk.CellRendererProgress, CellRendererButton,
gtk.CellRendererPixbuf)):
editable = False
else:
editable = renderer.get_property('editable')
if column.get_visible() and editable:
focus_column = column
break
if path[:-1]:
self.widget_tree.expand_to_path(path[:-1])
self.widget_tree.scroll_to_cell(path, focus_column, use_align=False)
self.widget_tree.set_cursor(path, focus_column, new)
@@ -691,7 +863,8 @@ class ViewList(ParserView):
if isinstance(renderer, CellRendererToggle):
renderer.set_property('activatable', False)
elif isinstance(renderer,
(gtk.CellRendererProgress, CellRendererButton)):
(gtk.CellRendererProgress, CellRendererButton,
gtk.CellRendererPixbuf)):
pass
else:
renderer.set_property('editable', False)
@@ -72,7 +72,7 @@ class EditableTreeView(gtk.TreeView):
idx = (current + i + 1) % len(cols)
if not cols[idx].get_cell_renderers():
continue
renderer = cols[idx].get_cell_renderers()[0]
renderer = cols[idx].get_cell_renderers()[-1]
if isinstance(renderer, CellRendererToggle):
editable = renderer.get_property('activatable')
elif isinstance(renderer,
@@ -91,7 +91,7 @@ class EditableTreeView(gtk.TreeView):
idx = (current - (i + 1)) % len(cols)
if not cols[idx].get_cell_renderers():
continue
renderer = cols[idx].get_cell_renderers()[0]
renderer = cols[idx].get_cell_renderers()[-1]
if isinstance(renderer, CellRendererToggle):
editable = renderer.get_property('activatable')
elif isinstance(renderer,
@@ -192,7 +192,7 @@ class EditableTreeView(gtk.TreeView):
elif event.keyval in (gtk.keysyms.Return,):
col = None
for column in self.get_columns():
renderer = column.get_cell_renderers()[0]
renderer = column.get_cell_renderers()[-1]
if isinstance(renderer, CellRendererToggle):
editable = renderer.get_property('activatable')
elif isinstance(renderer,
@@ -243,7 +243,6 @@ class EditableTreeView(gtk.TreeView):
entry.set_max_length(int(field.attrs.get('size', 0)))
# store in the record the entry widget to get the value in set_value
field.editabletree_entry = entry
record.modified = True
record.modified_fields.setdefault(column.name)
return False
@@ -66,8 +66,10 @@ def sort_model(column, treeview, screen):
class ParserTree(ParserInterface):
def __init__(self, window, parent=None, attrs=None, screen=None):
super(ParserTree, self).__init__(window, parent, attrs, screen)
def __init__(self, window, parent=None, attrs=None, screen=None,
children_field=None):
super(ParserTree, self).__init__(window, parent, attrs, screen,
children_field=children_field)
self.treeview = None
def parse(self, model_name, root_node, fields):
@@ -83,6 +85,7 @@ class ParserTree(ParserInterface):
treeview.cells = {}
treeview.sequence = attrs.get('sequence', False)
treeview.colors = attrs.get('colors', '"black"')
treeview.keyword_open = attrs.get('keyword_open', False)
self.treeview = treeview
treeview.set_property('rules-hint', True)
if not self.title:
@@ -124,7 +127,22 @@ class ParserTree(ParserInterface):
renderer.connect_after('editing-started', send_keys,
treeview)
col = gtk.TreeViewColumn(fields[fname].attrs['string'], renderer)
col = gtk.TreeViewColumn(fields[fname].attrs['string'])
if 'icon' in node_attrs:
render_pixbuf = gtk.CellRendererPixbuf()
col.pack_start(render_pixbuf, expand=False)
icon = node_attrs['icon']
def setter(column, cell, store, iter):
record = store.get_value(iter, 0)
value = record[icon].get_client(record) or ''
common.ICONFACTORY.register_icon(value)
pixbuf = treeview.render_icon(stock_id=value,
size=gtk.ICON_SIZE_BUTTON, detail=None)
cell.set_property('pixbuf', pixbuf)
col.set_cell_data_func(render_pixbuf, setter)
col.pack_start(renderer, expand=False)
col.name = fname
hbox = gtk.HBox(False, 2)
@@ -169,13 +187,16 @@ class ParserTree(ParserInterface):
col.set_fixed_width(width)
#XXX doesn't work well when resize columns
#col.set_expand(True)
if not treeview.sequence \
and fields[fname].attrs.get('sortable', True):
if (not treeview.sequence
and not self.children_field
and fields[fname].attrs.get('sortable', True)):
col.connect('clicked', sort_model, treeview, self.screen)
col.set_resizable(True)
col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
col.set_visible(not node_attrs.get('tree_invisible',
fields[fname].attrs.get('tree_invisible', False)))
if fname == self.screen.exclude_field:
col.set_visible(False)
i = treeview.append_column(col)
if 'sum' in node_attrs and fields[fname].attrs['type'] \
in ('integer', 'biginteger', 'float', 'numeric',
@@ -317,6 +338,8 @@ class Char(object):
raise NotImplementedError
def get_textual_value(self, record):
if not record:
return ''
return record[self.field_name].get_client(record) or ''
def value_from_text(self, record, text):
@@ -540,7 +563,7 @@ class M2O(Char):
return True, searched
return False, False
screen = Screen(relation, self.window, domain=domain, context=context,
view_type=['form'])
mode=['form'])
if obj_id:
screen.load([obj_id])
win = WinForm(screen, self.window)
@@ -605,7 +628,7 @@ class O2M(Char):
relation = field.attrs['relation']
context = field.context_get(record)
screen = Screen(relation, self.window, view_type=['tree', 'form'],
screen = Screen(relation, self.window, mode=['tree', 'form'],
exclude_field=field.attrs.get('relation_field'))
screen.group = group
win = WinForm(screen, self.window, view_type='tree', context=context)
@@ -897,7 +920,7 @@ class Button(object):
}, self.window, context=ctx)
else:
raise Exception('Unallowed button type')
self.screen.reload(writen=True)
self.screen.reload(written=True)
else:
self.screen.display()
@@ -27,19 +27,21 @@ PARSERS2 = {
class WidgetParse(ParserInterface):
def parse(self, screen, root_node, fields, toolbar=None):
def parse(self, screen, root_node, fields, toolbar=None,
children_field=None):
widget = None
for node in root_node.childNodes:
if not node.nodeType == node.ELEMENT_NODE:
continue
if node.localName in PARSERS:
widget = PARSERS[node.localName](self.window, self.parent,
self.attrs, screen)
self.attrs, screen, children_field)
wid, child, buttons, on_write, notebooks, cursor_widget = \
widget.parse(screen.model_name, node, fields)
screen.set_on_write(on_write)
res = PARSERS2[node.localName](self.window, screen, wid, child,
buttons, toolbar, notebooks, cursor_widget)
buttons, toolbar, notebooks, cursor_widget,
children_field)
res.title = widget.title
widget = res
break
@@ -201,16 +201,16 @@ class Parse(object):
class Form(object):
def __init__(self, xml, fields, model=None, parent=None, domain=None,
def __init__(self, view, model=None, parent=None, domain=None,
call=None, context=None):
if domain is None:
domain = []
if context is None:
context = {}
parser = Parse(parent, fields, model=model, context=context,
parser = Parse(parent, view['fields'], model=model, context=context,
on_change=self.on_change)
self.parent = parent
self.fields = fields
self.fields = view['fields']
self.model = model
self.parser = parser
self.call = call
@@ -219,7 +219,7 @@ class Form(object):
width = 640
if self.parent:
width = self.parent.size_request()[0]
(self.widgets, self.widget) = parser.parse(xml, width)
(self.widgets, self.widget) = parser.parse(view['arch'], width)
self.widget.show()
self.spin_limit = parser.spin_limit
self.spin_limit.connect('value-changed', self.limit_changed)
-4
View File
@@ -1,4 +0,0 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
from view_tree import *
from view_tree_sc import *
-98
View File
@@ -1,98 +0,0 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
"Parse"
import gtk
from xml.parsers import expat
import gettext
_ = gettext.gettext
class Parse(object):
"Parse"
def __init__(self, fields):
self.fields = fields
self.pixbufs = {}
self.fields_order = []
self.fields_attrs = {}
self.title = _('Tree')
self.toolbar = False
self.tree = None
self.pos = 1
self.fill = 0
def _psr_start(self, name, attrs):
if name == 'tree':
if 'string' in attrs:
self.title = attrs['string']
if 'toolbar' in attrs:
self.toolbar = bool(attrs.get('toolbar'))
if 'fill' in attrs:
self.fill = bool(int(attrs.get('fill')))
elif name == 'field':
if not attrs['name'] in self.fields:
return
field_type = self.fields[attrs['name']]['type']
if field_type != 'boolean':
column = gtk.TreeViewColumn(
self.fields[attrs['name']]['string'])
if 'icon' in attrs:
render_pixbuf = gtk.CellRendererPixbuf()
column.pack_start(render_pixbuf, expand=False)
column.add_attribute(render_pixbuf, 'pixbuf', self.pos)
self.fields_order.append(str(attrs['icon']))
self.pixbufs[self.pos] = True
self.pos += 1
cell = gtk.CellRendererText()
cell.set_fixed_height_from_font(1)
if field_type in ('float', 'numeric', 'integer',
'biginteger', 'float_time'):
cell.set_property('xalign', 1.0)
column.pack_start(cell, expand=False)
column.add_attribute(cell, 'text', self.pos)
else:
cell = gtk.CellRendererToggle()
column = gtk.TreeViewColumn(
self.fields[attrs['name']]['string'])
column.pack_start(cell, expand=False)
column.add_attribute(cell, 'active', self.pos)
self.pos += 1
self.fields_order.append(str(attrs['name']))
self.fields_attrs[attrs['name']] = attrs
column.set_resizable(True)
column.set_visible(not attrs.get('tree_invisible', False))
self.tree.append_column(column)
elif name == 'button':
pass
else:
import logging
log = logging.getLogger('view')
log.error('unknown tag: '+str(name))
del log
def _psr_end(self, name):
pass
def _psr_char(self, char):
pass
def parse(self, xml_data, tree):
"Parse"
cell = gtk.CellRendererText()
cell.set_fixed_height_from_font(1)
column = gtk.TreeViewColumn('ID', cell, text=0)
column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
column.set_fixed_width(60)
column.set_visible(False)
tree.append_column(column)
self.tree = tree
psr = expat.ParserCreate()
psr.StartElementHandler = self._psr_start
psr.EndElementHandler = self._psr_end
psr.CharacterDataHandler = self._psr_char
psr.Parse(xml_data)
if not self.fill:
column = gtk.TreeViewColumn()
tree.append_column(column)
return self.pos
-423
View File
@@ -1,423 +0,0 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
"View tree"
import gtk
import gobject
from tryton.config import CONFIG
import time
import tryton.rpc as rpc
from parse import Parse
import datetime
import locale
from tryton.common import HM_FORMAT
import tryton.common as common
from tryton.translate import date_format
from tryton.pyson import PYSONDecoder
from tryton.signal_event import SignalEvent
FIELDS_LIST_TYPE = {
'boolean': gobject.TYPE_BOOLEAN,
'integer': gobject.TYPE_INT,
'biginteger': gobject.TYPE_INT,
}
class ViewTreeModel(gtk.GenericTreeModel, gtk.TreeSortable):
def __init__(self, ids, view, fields, fields_type, fields_attrs,
context=None, pixbufs=None, treeview=None):
gtk.GenericTreeModel.__init__(self)
self.fields = fields
self.fields_type = fields_type
self.fields_attrs = fields_attrs
self.view = view
self.roots = ids
self.context = context or {}
self.to_reload = []
self.tree = self._node_process(self.roots)
self.pixbufs = pixbufs or {}
self.treeview = treeview
def _read(self, ids, fields):
ctx = {}
ctx.update(self.context)
ctx.update(rpc.CONTEXT)
res_ids = []
if ids:
args = ('model', self.view['model'], 'read', ids, fields, ctx)
try:
res_ids = rpc.execute(*args)
for obj_id in ids:
if obj_id in self.to_reload:
self.to_reload.remove(obj_id)
except Exception, exception:
for obj_id in ids:
val = {'id': obj_id}
for field in fields:
if field in self.fields_type \
and self.fields_type[field]['type'] \
in ('one2many', 'many2many'):
val[field] = []
else:
val[field] = ''
res_ids.append(val)
if obj_id not in self.to_reload:
self.to_reload.append(obj_id)
res_ids.sort(lambda x, y: cmp(ids.index(x['id']), ids.index(y['id'])))
for field in self.fields:
field_type = self.fields_type[field]['type']
if field in self.fields_attrs \
and 'widget' in self.fields_attrs[field]:
field_type = self.fields_attrs[field]['widget']
if field_type in ('date',):
display_format = date_format()
for obj in res_ids:
if obj[field]:
obj[field] = common.datetime_strftime(obj[field],
display_format)
elif field_type in ('datetime',):
display_format = date_format() + ' ' + HM_FORMAT
for obj in res_ids:
if obj[field]:
if 'timezone' in rpc.CONTEXT:
try:
import pytz
lzone = pytz.timezone(rpc.CONTEXT['timezone'])
szone = pytz.timezone(rpc.TIMEZONE)
sdt = szone.localize(obj[field], is_dst=True)
ldt = sdt.astimezone(lzone)
obj[field] = ldt
except Exception:
pass
obj[field] = common.datetime_strftime(obj[field],
display_format)
elif field_type in ('many2one', 'one2one'):
for obj in res_ids:
if obj[field]:
obj[field] = obj[field + '.rec_name']
elif field_type in ('selection'):
selection = self.fields_type[field]['selection']
if 'relation' in self.fields_type[field]:
try:
result = rpc.execute('model',
self.fields_type[field]['relation'],
'search_read',
self.fields_type[field].get('domain', []),
0, None, None,
['rec_name'], rpc.CONTEXT)
selection = [(x['id'], x['rec_name']) for x in result]
except Exception:
selection = []
else:
if not isinstance(self.fields_type[field]['selection'],
(list, tuple)):
try:
selection = rpc.execute('model',
self.view['model'],
self.fields_type[field]['selection'],
rpc.CONTEXT)
except Exception:
selection = []
self.fields_type[field]['selection'] = selection
elif field_type in ('float', 'numeric'):
digits = self.fields_type[field].get('digits', (16, 2))
for obj in res_ids:
if isinstance(digits, str):
digits = PYSONDecoder(obj).decode(digits)
obj[field] = locale.format('%.' + str(digits[1]) + 'f',
round(obj[field] or 0.0, digits[1]), True)
elif field_type in ('integer',):
for obj in res_ids:
obj[field] = locale.format('%d', obj[field] or 0, True)
elif field_type in ('float_time',):
conv = None
if 'float_time' in self.fields_attrs[field]:
conv = rpc.CONTEXT.get(
self.fields_attrs[field]['float_time'])
for obj in res_ids:
obj[field] = common.float_time_to_text(obj[field], conv)
elif field_type in ('boolean',):
for obj in res_ids:
obj[field] = bool(obj[field])
return res_ids
def _node_process(self, ids):
tree = []
fields = self.fields_type.keys()
for field_name in self.fields_type:
if self.fields_type[field_name]['type'] in ('many2one', 'one2one'):
fields.append(field_name + '.rec_name')
if self.view.get('field_childs', False):
res = self._read(ids, fields + [self.view['field_childs']])
for obj in res:
tree.append([obj['id'], None, [],
obj[self.view['field_childs']]])
tree[-1][1] = [obj[y] for y in self.fields]
if obj[self.view['field_childs']]:
tree[-1][2] = None
else:
res = self._read(ids, fields)
for obj in res:
tree.append([obj['id'], [obj[y] for y in self.fields], []])
return tree
def _node_expand(self, node):
node[2] = self._node_process(node[3])
#Mandatory GenericTreeModel method
def on_get_path(self, node):
'''returns the tree path (a tuple of indices)'''
return tuple([x[0] for x in node])
def on_get_flags(self):
return 0
def on_get_n_columns(self):
return len(self.fields)+1
def on_get_column_type(self, index):
if index in self.pixbufs:
return gtk.gdk.Pixbuf
if index == 0:
return gobject.TYPE_INT
return FIELDS_LIST_TYPE.get(
self.fields_type[self.fields[index-1]]['type'],
gobject.TYPE_STRING)
def on_get_tree_path(self, node):
'''returns the tree path (a tuple of indices)'''
return tuple([x[0] for x in node])
def on_get_iter(self, path):
'''returns the node corresponding to the given path.'''
node = []
tree = self.tree
for i in path:
if not tree or i >= len(tree):
return None
node.append((i, tree))
tree = tree[i][2]
return node
def on_get_value(self, node, column):
(i, values) = node[-1]
if column:
value = values[i][1][column - 1]
else:
return values[i][0]
res = value or ''
if (column in self.pixbufs) and res:
common.ICONFACTORY.register_icon(res)
return self.treeview.render_icon(stock_id=res,
size=gtk.ICON_SIZE_BUTTON, detail=None)
field = self.fields[column - 1]
field_type = self.fields_type[field]['type']
if field_type in ('selection'):
res = dict(self.fields_type[field]['selection']).get(res, '')
return res
def on_iter_next(self, node):
'''returns the next node at this level of the tree'''
node = node[:]
(i, values) = node[-1]
if i < len(values) - 1:
node[-1] = (i + 1, values)
return node
return None
def on_iter_children(self, node):
'''returns the first child of this node'''
if node is None:
return [(0, self.tree)]
node = node[:]
(i, values) = node[-1]
to_reload = False
if len(values[i]) >= 4:
for obj_id in values[i][3]:
if obj_id in self.to_reload:
to_reload = True
if values[i][2] is None or to_reload:
self._node_expand(values[i])
if values[i][2] == []:
return None
node.append((0, values[i][2]))
return node
def on_iter_has_child(self, node):
'''returns true if this node has children'''
(i, values) = node[-1]
return values[i][2] != []
def on_iter_n_children(self, node):
'''returns the number of children of this node'''
if node is None:
return len(self.tree)
(i, values) = node[-1]
to_reload = False
if len(values[i]) >= 4:
for obj_id in values[i][3]:
if obj_id in self.to_reload:
to_reload = True
if values[i][2] is None or to_reload:
self._node_expand(values[i])
return len(values[i][2])
def on_iter_nth_child(self, node, child):
'''returns the nth child of this node'''
if node is None:
if child < len(self.tree):
return [(child, self.tree)]
return None
node = node[:]
(i, values) = node[-1]
to_reload = False
if len(values[i]) >= 4:
for obj_id in values[i][3]:
if obj_id in self.to_reload:
to_reload = True
if values[i][2] is None or to_reload:
self._node_expand(values[i])
if child < len(values[i][2]):
node.append((child, values[i][2]))
return node
return None
def on_iter_parent(self, node):
'''returns the parent of this node'''
if node is None:
return None
return node[:-1]
def cus_refresh(self):
tree = self.tree
tree[0][2] = None
def _cus_row_find(self, ids_res):
tree = self.tree
try:
ids = ids_res[:]
while len(ids)>0:
if ids[-1] in self.roots:
ids.pop()
break
ids.pop()
path = []
while ids != []:
path.append(0)
val = ids.pop()
i = iter(tree)
while True:
node = i.next()
if node[0] == val:
break
path[-1] += 1
if (node[2] is None) and (ids != []):
return None
tree = node[2]
return (tuple(path), node)
except Exception:
return None
class ViewTree(SignalEvent):
"View tree"
def __init__(self, view_info, ids, window, sel_multi=False,
context=None):
super(ViewTree, self).__init__()
self.window = window
self.view = gtk.TreeView()
self.view.set_headers_visible(not CONFIG['client.modepda'])
self.context = {}
if context:
self.context.update(context)
self.fields = view_info['fields']
parse = Parse(self.fields)
parse.parse(view_info['arch'], self.view)
self.toolbar = parse.toolbar
self.pixbufs = parse.pixbufs
self.name = parse.title
self.sel_multi = sel_multi
selection = self.view.get_selection()
if sel_multi:
selection.set_mode(gtk.SELECTION_MULTIPLE)
else:
selection.self.view.get_selection().set_mode(gtk.SELECTION_SINGLE)
selection.connect('changed', self.__select_changed)
self.view.set_expander_column(self.view.get_column(1))
self.view.set_enable_search(False)
self.view.get_column(0).set_visible(False)
self.view.connect('key_press_event', self.on_keypress)
self.ids = ids
self.view_info = view_info
self.fields_order = parse.fields_order
self.fields_attrs = parse.fields_attrs
self.model = None
self.reload()
self.view.show_all()
self.search = []
self.next = 0
def on_keypress(self, widget, event):
if event.keyval in (gtk.keysyms.Down, gtk.keysyms.Up):
path, column = self.view.get_cursor()
if not path:
return False
store = self.view.get_model()
if event.keyval == gtk.keysyms.Down:
iter = store.get_iter(path)
if path[0] == len(store) - 1 \
and not store.iter_next(iter) \
and (not store.iter_has_child(iter) \
or not self.view.row_expanded(path)):
return True
elif event.keyval == gtk.keysyms.Up:
if path[0] == 0 \
and len(path) == 1:
return True
def reload(self):
self.model = ViewTreeModel(self.ids, self.view_info, self.fields_order,
self.fields, self.fields_attrs, context=self.context,
pixbufs=self.pixbufs, treeview=self.view)
self.view.set_model(self.model)
def widget_get(self):
return self.view
def sel_ids_get(self):
sel = self.view.get_selection()
if not sel:
return None
sel = sel.get_selected_rows()
if not sel:
return []
(model, iters) = sel
return [int(model.get_value(model.get_iter(x), 0)) for x in iters]
def sel_id_get(self):
res = self.sel_ids_get()
if res:
res = res[0]
return res
def value_get(self, col):
sel = self.view.get_selection().get_selected_rows()
if sel is None:
return None
(model, i) = sel
if not i:
return None
return model.get_value(i, col)
def __select_changed(self, tree_sel):
self.signal('select-changed')
@@ -1,75 +0,0 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
import tryton.rpc as rpc
import gobject
import gtk
import gettext
from tryton.gui import Main
import tryton.common as common
_ = gettext.gettext
class ViewTreeSC(object):
def __init__(self, tree, model, window):
self.model = model
self.tree = tree
self.window = window
self.tree.get_selection().set_mode('single')
column = gtk.TreeViewColumn (_('ID'), gtk.CellRendererText(), text=0)
self.tree.append_column(column)
column.set_visible(False)
cell = gtk.CellRendererText()
column = gtk.TreeViewColumn (_('Description'), cell, text=1)
self.tree.append_column(column)
self.tree.connect('key_press_event', self.on_keypress)
def on_keypress(self, widget, event):
if event.keyval in (gtk.keysyms.Down, gtk.keysyms.Up):
path, column = self.tree.get_cursor()
if not path:
return False
store = self.tree.get_model()
if event.keyval == gtk.keysyms.Down:
if path[0] == len(store) - 1:
return True
elif event.keyval == gtk.keysyms.Up:
if path[0] == 0:
return True
def update(self):
store = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING,
gobject.TYPE_STRING)
user = rpc._USER
args = ('model', 'ir.ui.view_sc', 'get_sc', user, self.model,
rpc.CONTEXT)
try:
view_sc = rpc.execute(*args)
except Exception, exception:
view_sc = common.process_exception(exception, self.window, *args)
if not view_sc:
return
for shortcut in view_sc:
num = store.append()
store.set(num, 0, shortcut['res_id'], 1, shortcut['name'],
2, shortcut['id'])
self.tree.set_model(store)
if self.model == 'ir.ui.menu':
Main.get_main().shortcut_set(shortcuts=view_sc)
def value_get(self, col):
sel = self.tree.get_selection().get_selected()
if sel is None:
return None
(model, i) = sel
if not i:
return None
return model.get_value(i, col)
def sel_id_get(self):
res = self.value_get(0)
if res is not None:
return int(res)
return None
+2 -2
View File
@@ -268,7 +268,7 @@ class WinForm(object):
def _sig_label(self, screen, signal_data):
name = '_'
if signal_data[0] >= 0:
name = str(signal_data[0] + 1)
name = str(signal_data[0])
line = '(%s/%s)' % (name, signal_data[1])
self.label.set_text(line)
@@ -303,7 +303,7 @@ class WinForm(object):
if res == gtk.RESPONSE_OK:
return True
elif res == gtk.RESPONSE_CANCEL:
self.screen.remove(delete=True)
self.screen.remove()
return False
def new(self):
+2 -2
View File
@@ -48,7 +48,7 @@ class WinSearch(object):
self.win.vbox.pack_start(scrollwindow, expand=True, fill=True)
self.screen = Screen(model, self.win, domain=domain,
view_type=['tree'], context=context,
mode=['tree'], context=context,
views_preload=views_preload, row_activate=self.sig_activate)
self.view = self.screen.current_view
self.view.unset_editable()
@@ -104,7 +104,7 @@ class WinSearch(object):
elif button == gtk.RESPONSE_ACCEPT:
res = None
screen = Screen(self.model_name, self.win, domain=self.domain,
context=self.context, view_type=['form'])
context=self.context, mode=['form'])
win = WinForm(screen, self.win, new=True)
while win.run():
if screen.save_current():
+8 -21
View File
@@ -7,35 +7,22 @@ from preference import *
class Window(object):
@staticmethod
def create(view_ids, model, res_id=False, domain=None,
view_type='form', window=None, context=None, mode=None, name=False,
limit=None, auto_refresh=False, search_value=None, icon=None):
def create(view_ids, model, res_id=False, domain=None, window=None,
context=None, mode=None, name=False, limit=None,
auto_refresh=False, search_value=None, icon=None):
from tryton.gui import Main
if context is None:
context = {}
if view_type not in ('form', 'tree', 'board'):
import logging
log = logging.getLogger('view')
log.error('unknown view type: '+view_type)
del log
return
if view_type == 'form':
if model:
from form import Form
win = Form(model, window, res_id, domain, view_type=mode,
view_ids = (view_ids or []), context=context, name=name,
win = Form(model, window, res_id, domain, mode=mode,
view_ids=(view_ids or []), context=context, name=name,
limit=limit, auto_refresh=auto_refresh,
search_value=search_value)
elif view_type == 'tree':
if model == 'ir.ui.menu':
if Main.get_main().sig_reload_menu():
return
from tree import Tree
win = Tree(model, window, res_id, view_ids and view_ids[0] or None,
domain, context, name=name)
elif view_type == 'board':
else:
from board import Board
win = Board(window, view_ids and view_ids[0] or None,
win = Board(model, window, view_ids and view_ids[0] or None,
context=context, name=name, auto_refresh=auto_refresh)
win.icon = icon
Main.get_main().win_add(win)
+5 -6
View File
@@ -83,8 +83,7 @@ class Wizard(SignalEvent):
elif res['type'] == 'form':
self.datas['form'] = {}
if res['type'] == 'form':
self.update(res['arch'], res['fields'], res['state'],
res['object'], context=ctx)
self.update(res, res['state'], res['object'], context=ctx)
self.screen.current_record.set_default(self.datas['form'])
break
elif res['type'] == 'action':
@@ -148,7 +147,7 @@ class Wizard(SignalEvent):
self.state = state
self.process()
def update(self, arch, fields, state, obj_name, context=None):
def update(self, view, state, obj_name, context=None):
self.model = obj_name
hbuttonbox = gtk.HButtonBox()
@@ -170,13 +169,13 @@ class Wizard(SignalEvent):
hbuttonbox.pack_start(but)
val = {}
fields = view['fields']
for i in fields:
if 'value' in fields[i]:
val[i] = fields[i]['value']
self.screen = Screen(obj_name, self.window, view_type=[],
context=context)
self.screen.add_view(arch, fields, display=True)
self.screen = Screen(obj_name, self.window, mode=[], context=context)
self.screen.add_view(view, display=True)
self.screen.widget.show()
title = gtk.Label()
+1 -1
View File
@@ -31,7 +31,7 @@ class SignalEvent(object):
for sig in signal:
i = 0
while i < len(self.__connects[sig]):
if self.__connects[sig][i][2] == key:
if self.__connects[sig][i][2] is key:
del self.__connects[sig][i]
else:
i += 1
+6 -6
View File
@@ -16,7 +16,7 @@ _ = gettext.gettext
class Dialog(object):
"Dialog for wizard"
def __init__(self, arch, fields, state, obj_name, parent,
def __init__(self, view, state, obj_name, parent,
action='', size=(0, 0), context=None):
self.parent = parent
self.action = action
@@ -52,12 +52,13 @@ class Dialog(object):
self.states.append(i[0])
val = {}
fields = view['fields']
for i in fields:
if 'value' in fields[i]:
val[i] = fields[i]['value']
self.screen = Screen(obj_name, self.dia, view_type=[], context=context)
self.screen.add_view(arch, fields, display=True)
self.screen = Screen(obj_name, self.dia, mode=[], context=context)
self.screen.add_view(view, display=True)
title = gtk.Label()
title.set_use_markup(True)
@@ -216,9 +217,8 @@ class Wizard(object):
datas['form'] = {}
if res['type'] == 'form':
if not dia:
dia = Dialog(res['arch'], res['fields'], res['state'],
res['object'], parent, action=action,
size=res['size'], context=ctx)
dia = Dialog(res, res['state'], res['object'], parent,
action=action, size=res['size'], context=ctx)
dia.screen.current_record.set_default(datas['form'])
res2 = dia.run(datas['form'])
if not res2: