1 /*---------------------------------------------------------
2 * OpenERP diagram library
3 *---------------------------------------------------------*/
5 openerp.web_diagram = function (instance) {
6 var QWeb = instance.web.qweb,
8 _lt = instance.web._lt;
9 instance.web.views.add('diagram', 'instance.web.DiagramView');
10 instance.web.DiagramView = instance.web.View.extend({
11 display_name: _lt('Diagram'),
13 init: function(parent, dataset, view_id, options) {
15 this.set_default_options(options);
16 this.view_manager = parent;
17 this.dataset = dataset;
18 this.model = this.dataset.model;
19 this.view_id = view_id;
20 this.domain = this.dataset._domain || [];
22 this.ids = this.dataset.ids;
25 return this.rpc("/web_diagram/diagram/load", {"model": this.model, "view_id": this.view_id}, this.on_loaded);
28 toTitleCase: function(str) {
29 return str.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();});
32 on_loaded: function(result) {
35 if(this.ids && this.ids.length) {
36 this.id = this.ids[self.dataset.index || 0];
39 this.fields_view = result.fields_view,
40 this.view_id = this.fields_view.view_id,
41 this.fields = this.fields_view.fields,
42 this.nodes = this.fields_view.arch.children[0],
43 this.connectors = this.fields_view.arch.children[1],
44 this.node = this.nodes.attrs.object,
45 this.connector = this.connectors.attrs.object;
47 this.$element.html(QWeb.render("DiagramView", this));
49 this.$element.find('div.oe_diagram_pager button[data-pager-action]').click(function() {
50 var action = $(this).data('pager-action');
51 self.on_pager_action(action);
54 this.do_update_pager();
57 this.$element.find('#new_node.oe_diagram_button_new').click(function(){self.add_node();});
60 self.get_diagram_info();
65 get_diagram_info: function() {
71 'connector': this.connector,
72 'bgcolor': this.nodes.attrs.bgcolor,
73 'shape': this.nodes.attrs.shape,
74 'src_node': this.connectors.attrs.source,
75 'des_node': this.connectors.attrs.destination,
76 'label': this.connectors.attrs.label || false,
78 'invisible_nodes': [],
81 'connectors_fields': []
84 _.each(this.nodes.children, function(child) {
85 if(child.attrs.invisible == '1')
86 params['invisible_nodes'].push(child.attrs.name);
88 params['visible_nodes'].push(child.attrs.name);
89 params['node_fields'].push(self.fields[child.attrs.name]['string']|| this.toTitleCase(child.attrs.name));
93 _.each(this.connectors.children, function(conn) {
94 params['connectors_fields'].push(self.fields[conn.attrs.name]['string']|| this.toTitleCase(conn.attrs.name));
95 params['connectors'].push(conn.attrs.name);
99 '/web_diagram/diagram/get_diagram_info',params,
101 self.draw_diagram(result);
106 on_diagram_loaded: function(record) {
107 var id_record = record['id'];
110 this.get_diagram_info();
114 // Set-up the drawing elements of the diagram
115 draw_diagram: function(result) {
117 var res_nodes = result['nodes'];
118 var res_edges = result['conn'];
119 this.parent_field = result.parent_field;
120 this.$element.find('h3.oe_diagram_title').text(result.name);
126 edge_color: "#A0A0A0",
127 edge_label_color: "#555",
128 edge_label_font_size: 10,
131 edge_loop_radius: 100,
133 node_label_color: "#333",
134 node_label_font_size: 12,
135 node_outline_color: "#333",
136 node_outline_width: 1,
137 node_selected_color: "#0097BE",
138 node_selected_width: 2,
141 connector_active_color: "#FFF",
144 close_button_radius: 8,
145 close_button_color: "#333",
146 close_button_x_color: "#FFF",
154 // remove previous diagram
155 var canvas = self.$element.find('div.oe_diagram_diagram')
158 var r = new Raphael(canvas, '100%','100%');
160 var graph = new CuteGraph(r,style,canvas.parentNode);
162 var confirm_dialog = $('#dialog').dialog({
164 title: _t("Are you sure?") });
167 _.each(res_nodes, function(node) {
168 var n = new CuteNode(
170 node.x + 50, //FIXME the +50 should be in the layout algorithm
172 CuteGraph.wordwrap(node.name, 14),
173 node.shape === 'rectangle' ? 'rect' : 'circle',
174 node.color === 'white' ? style.white : style.gray);
177 id_to_node[node.id] = n;
180 _.each(res_edges, function(edge) {
181 var e = new CuteEdge(
183 CuteGraph.wordwrap(edge.signal, 32),
184 id_to_node[edge.s_id],
185 id_to_node[edge.d_id] || id_to_node[edge.s_id] ); //WORKAROUND
189 CuteNode.double_click_callback = function(cutenode){
190 self.edit_node(cutenode.id);
193 CuteNode.destruction_callback = function(cutenode){
194 if(!confirm(_t("Deleting this node cannot be undone.\nIt will also delete all connected transitions.\n\nAre you sure ?"))){
195 return $.Deferred().reject().promise();
197 return new instance.web.DataSet(self,self.node).unlink([cutenode.id]);
199 CuteEdge.double_click_callback = function(cuteedge){
200 self.edit_connector(cuteedge.id);
203 CuteEdge.creation_callback = function(node_start, node_end){
204 return {label:_t("")};
206 CuteEdge.new_edge_callback = function(cuteedge){
207 self.add_connector(cuteedge.get_start().id,
208 cuteedge.get_end().id,
211 CuteEdge.destruction_callback = function(cuteedge){
212 if(!confirm(_t("Deleting this transition cannot be undone.\n\nAre you sure ?"))){
213 return $.Deferred().reject().promise();
215 return new instance.web.DataSet(self,self.connector).unlink([cuteedge.id]);
220 // Creates a popup to edit the content of the node with id node_id
221 edit_node: function(node_id){
223 var title = _t('Activity');
224 var pop = new instance.web.form.FormOpenPopup(self);
229 self.context || self.dataset.context,
231 title: _t("Open: ") + title
235 pop.on_write.add(function() {
236 self.dataset.read_index(_.keys(self.fields_view.fields)).pipe(self.on_diagram_loaded);
239 var form_fields = [self.parent_field];
240 var form_controller = pop.view_form;
242 form_controller.on_record_loaded.add_first(function() {
243 _.each(form_fields, function(fld) {
244 if (!(fld in form_controller.fields)) { return; }
245 var field = form_controller.fields[fld];
246 field.$input.prop('disabled', true);
247 field.$drop_down.unbind();
248 field.$menu_btn.unbind();
253 // Creates a popup to add a node to the diagram
254 add_node: function(){
256 var title = _t('Activity');
257 var pop = new instance.web.form.SelectCreatePopup(self);
261 title: _t("Create:") + title,
262 initial_view: 'form',
263 disable_multiple_selection: true
266 self.context || self.dataset.context
268 pop.on_select_elements.add_last(function(element_ids) {
269 self.dataset.read_index(_.keys(self.fields_view.fields)).pipe(self.on_diagram_loaded);
272 var form_controller = pop.view_form;
273 var form_fields = [this.parent_field];
275 form_controller.on_record_loaded.add_last(function() {
276 _.each(form_fields, function(fld) {
277 if (!(fld in form_controller.fields)) { return; }
278 var field = form_controller.fields[fld];
279 field.set_value(self.id);
285 // Creates a popup to edit the connector of id connector_id
286 edit_connector: function(connector_id){
288 var title = _t('Transition');
289 var pop = new instance.web.form.FormOpenPopup(self);
292 parseInt(connector_id,10), //FIXME Isn't connector_id supposed to be an int ?
293 self.context || self.dataset.context,
295 title: _t("Open: ") + title
298 pop.on_write.add(function() {
299 self.dataset.read_index(_.keys(self.fields_view.fields)).pipe(self.on_diagram_loaded);
303 // Creates a popup to add a connector from node_source_id to node_dest_id.
304 // dummy_cuteedge if not null, will be removed form the graph after the popup is closed.
305 add_connector: function(node_source_id, node_dest_id, dummy_cuteedge){
307 var title = _t('Transition');
308 var pop = new instance.web.form.SelectCreatePopup(self);
313 title: _t("Create:") + title,
314 initial_view: 'form',
315 disable_multiple_selection: true
318 this.context || this.dataset.context
321 pop.on_select_elements.add_last(function(element_ids) {
322 self.dataset.read_index(_.keys(self.fields_view.fields)).pipe(self.on_diagram_loaded);
324 // We want to destroy the dummy edge after a creation cancel. This destroys it even if we save the changes.
325 // This is not a problem since the diagram is completely redrawn on saved changes.
326 pop.$element.bind("dialogbeforeclose",function(){
328 dummy_cuteedge.remove();
332 var form_controller = pop.view_form;
334 form_controller.on_record_loaded.add_last(function () {
335 form_controller.fields[self.connectors.attrs.source].set_value(node_source_id);
336 form_controller.fields[self.connectors.attrs.source].dirty = true;
337 form_controller.fields[self.connectors.attrs.destination].set_value(node_dest_id);
338 form_controller.fields[self.connectors.attrs.destination].dirty = true;
342 on_pager_action: function(action) {
345 this.dataset.index = 0;
348 this.dataset.previous();
354 this.dataset.index = this.dataset.ids.length - 1;
357 var loaded = this.dataset.read_index(_.keys(this.fields_view.fields))
358 .pipe(this.on_diagram_loaded);
359 this.do_update_pager();
363 do_update_pager: function(hide_index) {
364 var $pager = this.$element.find('div.oe_diagram_pager');
365 var index = hide_index ? '-' : this.dataset.index + 1;
366 if(!this.dataset.count) {
367 this.dataset.count = this.dataset.ids.length;
369 $pager.find('span.oe_pager_index').html(index);
370 $pager.find('span.oe_pager_count').html(this.dataset.count);
373 do_show: function() {
374 this.do_push_state({});
375 return $.when(this._super(), this.on_pager_action('reload'));
380 // vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax: