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'),
14 init: function(parent, dataset, view_id, options) {
17 this.set_default_options(options);
18 this.view_manager = parent;
19 this.dataset = dataset;
20 this.model = this.dataset.model;
21 this.view_id = view_id;
22 this.domain = this.dataset._domain || [];
24 this.ids = this.dataset.ids;
25 this.on('pager_action_executed', self, self.pager_action_trigger);
28 view_loading: function(r) {
29 return this.load_diagram(r);
32 toTitleCase: function(str) {
33 return str.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();});
36 load_diagram: function(result) {
38 if(this.ids && this.ids.length) {
39 this.id = this.ids[self.dataset.index || 0];
42 this.fields_view = result,
43 this.view_id = this.fields_view.view_id,
44 this.fields = this.fields_view.fields,
45 this.nodes = this.fields_view.arch.children[0],
46 this.connectors = this.fields_view.arch.children[1],
47 this.node = this.nodes.attrs.object,
48 this.connector = this.connectors.attrs.object;
49 this.labels = _.filter(this.fields_view.arch.children, function(label) {
50 return label.tag == "label";
53 this.$el.html(QWeb.render("DiagramView", {'widget': self}));
54 this.$el.addClass(this.fields_view.arch.attrs['class']);
56 _.each(self.labels,function(label){
57 html_label = '<p style="padding: 4px;">' + label.attrs.string + "</p>";
58 self.$el.find('.oe_diagram_header').append(html_label);
64 this.$el.find('#new_node.oe_diagram_button_new').click(function(){self.add_node();});
67 self.get_diagram_info();
69 this.trigger('diagram_view_loaded', result);
72 get_diagram_info: function() {
78 'connector': this.connector,
79 'bgcolor': this.nodes.attrs.bgcolor,
80 'shape': this.nodes.attrs.shape,
81 'src_node': this.connectors.attrs.source,
82 'des_node': this.connectors.attrs.destination,
83 'label': this.connectors.attrs.label || false,
85 'invisible_nodes': [],
88 'connectors_fields': []
91 _.each(this.nodes.children, function(child) {
92 if(child.attrs.invisible == '1')
93 params['invisible_nodes'].push(child.attrs.name);
95 params['visible_nodes'].push(child.attrs.name);
96 params['node_fields'].push(self.fields[child.attrs.name]['string']|| this.toTitleCase(child.attrs.name));
100 _.each(this.connectors.children, function(conn) {
101 params['connectors_fields'].push(self.fields[conn.attrs.name]['string']|| this.toTitleCase(conn.attrs.name));
102 params['connectors'].push(conn.attrs.name);
105 '/web_diagram/diagram/get_diagram_info',params).done(function(result) {
106 self.draw_diagram(result);
111 on_diagram_loaded: function(record) {
112 var id_record = record['id'];
115 this.get_diagram_info();
116 this.do_push_state({id: id_record});
118 this.do_push_state({});
122 do_load_state: function(state, warm) {
123 if (state && state.id) {
124 if (!this.dataset.get_id_index(state.id)) {
125 this.dataset.ids.push(state.id);
127 this.dataset.select_id(state.id);
134 // Set-up the drawing elements of the diagram
135 draw_diagram: function(result) {
137 var res_nodes = result['nodes'];
138 var res_edges = result['conn'];
139 this.parent_field = result.parent_field;
140 this.$el.find('h3.oe_diagram_title').text(result.name);
146 edge_color: "#A0A0A0",
147 edge_label_color: "#555",
148 edge_label_font_size: 10,
151 edge_loop_radius: 100,
153 node_label_color: "#333",
154 node_label_font_size: 12,
155 node_outline_color: "#333",
156 node_outline_width: 1,
157 node_selected_color: "#0097BE",
158 node_selected_width: 2,
161 connector_active_color: "#FFF",
164 close_button_radius: 8,
165 close_button_color: "#333",
166 close_button_x_color: "#FFF",
174 // remove previous diagram
175 var canvas = self.$el.find('div.oe_diagram_diagram')
178 var r = new Raphael(canvas, '100%','100%');
180 var graph = new CuteGraph(r,style,canvas.parentNode);
182 _.each(res_nodes, function(node) {
183 var n = new CuteNode(
185 node.x + 50, //FIXME the +50 should be in the layout algorithm
187 CuteGraph.wordwrap(node.name, 14),
188 node.shape === 'rectangle' ? 'rect' : 'circle',
189 node.color === 'white' ? style.white : style.gray);
192 id_to_node[node.id] = n;
195 _.each(res_edges, function(edge) {
196 var e = new CuteEdge(
198 CuteGraph.wordwrap(edge.signal, 32),
199 id_to_node[edge.s_id],
200 id_to_node[edge.d_id] || id_to_node[edge.s_id] ); //WORKAROUND
204 CuteNode.double_click_callback = function(cutenode){
205 self.edit_node(cutenode.id);
208 CuteNode.destruction_callback = function(cutenode){
209 if(!confirm(_t("Deleting this node cannot be undone.\nIt will also delete all connected transitions.\n\nAre you sure ?"))){
210 return $.Deferred().reject().promise();
212 return new instance.web.DataSet(self,self.node).unlink([cutenode.id]);
214 CuteEdge.double_click_callback = function(cuteedge){
215 self.edit_connector(cuteedge.id);
218 CuteEdge.creation_callback = function(node_start, node_end){
219 return {label:_t("")};
221 CuteEdge.new_edge_callback = function(cuteedge){
222 self.add_connector(cuteedge.get_start().id,
223 cuteedge.get_end().id,
226 CuteEdge.destruction_callback = function(cuteedge){
227 if(!confirm(_t("Deleting this transition cannot be undone.\n\nAre you sure ?"))){
228 return $.Deferred().reject().promise();
230 return new instance.web.DataSet(self,self.connector).unlink([cuteedge.id]);
235 // Creates a popup to edit the content of the node with id node_id
236 edit_node: function(node_id){
238 var title = _t('Activity');
239 var pop = new instance.web.form.FormOpenPopup(self);
244 self.context || self.dataset.context,
246 title: _t("Open: ") + title
250 pop.on('write_completed', self, function() {
251 self.dataset.read_index(_.keys(self.fields_view.fields)).then(self.on_diagram_loaded);
254 var form_fields = [self.parent_field];
255 var form_controller = pop.view_form;
257 form_controller.on("load_record", self, function(){
258 _.each(form_fields, function(fld) {
259 if (!(fld in form_controller.fields)) { return; }
260 var field = form_controller.fields[fld];
261 field.$input.prop('disabled', true);
262 field.$drop_down.unbind();
269 // Creates a popup to add a node to the diagram
270 add_node: function(){
272 var title = _t('Activity');
273 var pop = new instance.web.form.SelectCreatePopup(self);
277 title: _t("Create:") + title,
278 initial_view: 'form',
279 disable_multiple_selection: true
282 self.context || self.dataset.context
284 pop.on("elements_selected", self, function(element_ids) {
285 self.dataset.read_index(_.keys(self.fields_view.fields)).then(self.on_diagram_loaded);
288 var form_controller = pop.view_form;
289 var form_fields = [this.parent_field];
291 form_controller.on("load_record", self, function(){
292 _.each(form_fields, function(fld) {
293 if (!(fld in form_controller.fields)) { return; }
294 var field = form_controller.fields[fld];
295 field.set_value(self.id);
301 // Creates a popup to edit the connector of id connector_id
302 edit_connector: function(connector_id){
304 var title = _t('Transition');
305 var pop = new instance.web.form.FormOpenPopup(self);
308 parseInt(connector_id,10), //FIXME Isn't connector_id supposed to be an int ?
309 self.context || self.dataset.context,
311 title: _t("Open: ") + title
314 pop.on('write_completed', self, function() {
315 self.dataset.read_index(_.keys(self.fields_view.fields)).then(self.on_diagram_loaded);
319 // Creates a popup to add a connector from node_source_id to node_dest_id.
320 // dummy_cuteedge if not null, will be removed form the graph after the popup is closed.
321 add_connector: function(node_source_id, node_dest_id, dummy_cuteedge){
323 var title = _t('Transition');
324 var pop = new instance.web.form.SelectCreatePopup(self);
329 title: _t("Create:") + title,
330 initial_view: 'form',
331 disable_multiple_selection: true
334 this.context || this.dataset.context
336 pop.on("elements_selected", self, function(element_ids) {
337 self.dataset.read_index(_.keys(self.fields_view.fields)).then(self.on_diagram_loaded);
339 // We want to destroy the dummy edge after a creation cancel. This destroys it even if we save the changes.
340 // This is not a problem since the diagram is completely redrawn on saved changes.
341 pop.$el.parents('.modal').on('hidden.bs.modal', function (e){
343 dummy_cuteedge.remove();
347 var form_controller = pop.view_form;
350 form_controller.on("load_record", self, function(){
351 form_controller.fields[self.connectors.attrs.source].set_value(node_source_id);
352 form_controller.fields[self.connectors.attrs.source].dirty = true;
353 form_controller.fields[self.connectors.attrs.destination].set_value(node_dest_id);
354 form_controller.fields[self.connectors.attrs.destination].dirty = true;
358 do_hide: function () {
365 init_pager: function() {
368 this.$pager.remove();
370 this.$pager = $(QWeb.render("DiagramView.pager", {'widget':self})).hide();
371 if (this.options.$pager) {
372 this.$pager.appendTo(this.options.$pager);
374 this.$el.find('.oe_diagram_pager').replaceWith(this.$pager);
376 this.$pager.on('click','a[data-pager-action]',function() {
377 var action = $(this).data('pager-action');
378 self.execute_pager_action(action);
380 this.do_update_pager();
383 pager_action_trigger: function(){
384 var loaded = this.dataset.read_index(_.keys(this.fields_view.fields))
385 .then(this.on_diagram_loaded);
386 this.do_update_pager();
390 execute_pager_action: function(action) {
393 this.dataset.index = 0;
396 this.dataset.previous();
402 this.dataset.index = this.dataset.ids.length - 1;
405 this.trigger('pager_action_executed');
409 do_update_pager: function(hide_index) {
410 this.$pager.toggle(this.dataset.ids.length > 1);
412 $(".oe_diagram_pager_state", this.$pager).html("");
414 $(".oe_diagram_pager_state", this.$pager).html(_.str.sprintf(_t("%d / %d"), this.dataset.index + 1, this.dataset.ids.length));
418 do_show: function() {
419 this.do_push_state({});
420 return $.when(this._super(), this.execute_pager_action('reload'));
425 // vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax: