d86870005a6a7744d28faeae7988ce5b8a9da92d
[odoo/odoo.git] / addons / base / static / openerp / js / base_views.js
1 /*---------------------------------------------------------
2  * OpenERP base library
3  *---------------------------------------------------------*/
4
5 openerp.base$views = function(openerp) {
6
7 openerp.base.Action =  openerp.base.Controller.extend({
8     init: function(session, element_id) {
9         this._super(session, element_id);
10         this.action = null;
11         this.dataset = null;
12         this.searchview_id = false;
13         this.searchview = null;
14         this.listview_id = false;
15         this.listview = null;
16         this.formview_id = false;
17         this.formview = null;
18     },
19     start: function() {
20         this.$element.html(QWeb.render("Action", {"prefix":this.element_id}));
21         this.$element.find("#mode_list").bind('click',this.on_mode_list);
22         this.$element.find("#mode_form").bind('click',this.on_mode_form);
23         this.on_mode_list();
24     },
25     on_mode_list: function() {
26         $("#oe_action_form").hide();
27         $("#oe_action_search").show();
28         $("#oe_action_list").show();
29     },
30     on_mode_form: function() {
31         $("#oe_action_form").show();
32         $("#oe_action_search").hide();
33         $("#oe_action_list").hide();
34     },
35     do_action: function(action) {
36         // instantiate the right controllers by understanding the action
37         this.action = action;
38         this.log(action);
39 //        debugger;
40         //this.log(action);
41         if(action.type == "ir.actions.act_window") {
42             this.do_action_window(action);
43         }
44     },
45     do_action_window: function(action) {
46         this.formview_id = false;
47         this.dataset = new openerp.base.DataSet(this.session, "oe_action_dataset", action.res_model);
48         this.dataset.start();
49
50         // Locate first tree view
51         this.listview_id = false;
52         for(var i = 0; i < action.views.length; i++)  {
53             if(action.views[i][1] == "tree") {
54                 this.listview_id = action.views[i][0];
55                 break;
56             }
57         }
58         this.listview = new openerp.base.ListView(this.session, "oe_action_list", this.dataset, this.listview_id);
59         this.listview.start();
60
61         // Locate first form view
62         this.listview_id = false;
63         for(var j = 0; j < action.views.length; j++)  {
64             if(action.views[j][1] == "form") {
65                 this.formview_id = action.views[j][0];
66                 break;
67             }
68         }
69         this.formview = new openerp.base.FormView(this.session, "oe_action_form", this.dataset, this.formview_id);
70         this.formview.start();
71
72         // Take the only possible search view. Is that consistent ?
73         this.searchview_id = false;
74         if(this.listview && action.search_view_id) {
75             this.searchview_id = action.search_view_id[0];
76         }
77         this.searchview = new openerp.base.SearchView(this.session, "oe_action_search", this.dataset, this.searchview_id);
78         this.searchview.start();
79     }
80 });
81
82 openerp.base.View = openerp.base.Controller.extend({
83 // to replace Action
84 });
85
86 openerp.base.DataSet =  openerp.base.Controller.extend({
87     init: function(session, element_id, model) {
88         this._super(session, element_id);
89         this.model = model;
90         this.model_fields = null;
91         this.fields = [];
92         // SHOULD USE THE ONE FROM FIELDS VIEW GET BECAUSE OF SELECTION
93         this.domain = [];
94         this.context = {};
95         this.order = "";
96         this.count = null;
97         this.ids = [];
98         this.values = {};
99 /*
100     group_by
101         rows record
102             fields of row1 field fieldname
103                 { type: value: text: text_format: text_completions type_*: a
104 */
105     },
106     start: function() {
107         this.rpc("/base/dataset/fields", {"model":this.model}, this.on_fields);
108     },
109     on_fields: function(result) {
110         this.model_fields = result.fields;
111         this.on_ready();
112     },
113     do_load: function(offset, limit) {
114         this.rpc("/base/dataset/load", {model: this.model, fields: this.fields }, this.on_loaded);
115     },
116     on_loaded: function(data) {
117         this.ids = data.ids;
118         this.values = data.values;
119     },
120     on_reloaded: function(ids) {
121     }
122 });
123
124 openerp.base.DataRecord =  openerp.base.Controller.extend({
125     init: function(session,  model, fields) {
126         this._super(session, null);
127         this.model = model;
128         this.id = id;
129         this.fields = fields;
130         this.values = {};
131     },
132     load: function(id) {
133         this.rpc("/base/datarecord/load", {"model": this.model, "id": this.id, "fields": "todo"}, this.on_loaded);
134     },
135     on_loaded: function(result) {
136         this.values = result.values;
137     },
138     on_change: function() {
139     },
140     on_reload: function() {
141     }
142 });
143
144 openerp.base.SearchView = openerp.base.Controller.extend({
145     init: function(session, element_id, dataset, view_id) {
146         this._super(session, element_id);
147         this.dataset = dataset;
148         this.model = dataset.model;
149         this.view_id = view_id;
150         this.input_index = 0;
151         this.input_ids = {};
152         this.domain = [];
153     },
154     start: function() {
155         //this.log('Starting SearchView '+this.model+this.view_id)
156         this.rpc("/base/searchview/load", {"model": this.model, "view_id":this.view_id}, this.on_loaded);
157     },
158     on_loaded: function(data) {
159         this.fields_view = data.fields_view;
160         this.log(this.fields_view);
161         this.input_ids = {};
162         this.$element.html(QWeb.render("SearchView", {"fields_view": this.fields_view}));
163         this.$element.find("#search").bind('click',this.on_search);
164         // TODO bind click event on all button
165         // TODO we don't do many2one yet, but in the future bind a many2one controller on them
166         this.log(this.$element.find("#search"));
167     },
168     register_input: function(node) {
169         // self should be passed in the qweb dict to do:
170         // <input t-add-id="self.register_input(node)"/>
171
172         // generate id
173         var id = this.element_id + "_" + this.input_index++;
174         // TODO construct a nice object
175         // save it in our registry
176         this.input_ids[id] = {
177             node: node,
178             type: "filter",
179             domain: "",
180             context: "",
181             disabled: false
182         };
183
184         return id;
185     },
186     on_click: function() {
187         // event catched on a button
188         // flip the disabled flag
189         // adjust the css class
190     },
191     on_search: function() {
192         this.log("on_search");
193         // collect all non disabled domains definitions, AND them
194         // evaluate as python expression
195         // save the result in this.domain
196         this.dataset.do_load();
197     },
198     on_clear: function() {
199     }
200 });
201
202 openerp.base.SearchViewInput = openerp.base.Controller.extend({
203 // TODO not sure should we create a controller for every input ?
204
205 // of we just keep a simple dict for each input in
206 // openerp.base.SearchView#input_ids
207 // and use if when we get an event depending on the type
208 // i think it's less bloated to avoid useless controllers
209
210 // but i think for many2one a controller would be nice
211 // so simple dict for simple inputs
212 // an controller for many2one ?
213
214 });
215
216 openerp.base.FormView =  openerp.base.Controller.extend({
217     init: function(session, element_id, dataset, view_id) {
218         this._super(session, element_id);
219         this.dataset = dataset;
220         this.dataset_index = 0;
221         this.model = dataset.model;
222         this.view_id = view_id;
223         this.fields_views = {};
224         this.widgets = {};
225         this.fields = {};
226         this.datarecord = {};
227     },
228     start: function() {
229         //this.log('Starting FormView '+this.model+this.view_id)
230         this.rpc("/base/formview/load", {"model": this.model, "view_id": this.view_id}, this.on_loaded);
231     },
232     on_loaded: function(data) {
233         this.fields_view = data.fields_view;
234         //this.log(this.fields_view);
235
236         var frame = new openerp.base.WidgetFrame(this.session, null, this, this.fields_view.arch);
237
238         this.$element.html(QWeb.render("FormView", { "frame": frame, "view": this }));
239         for (var i in this.widgets) {
240             this.widgets[i].register();
241         }
242         // bind to all wdigets that have onchange ??
243
244         // When the dataset is loaded load the first record (like gtk)
245         this.dataset.on_loaded.add_last(this.do_load_record);
246
247         // When a datarecord is loaded display the values in the inputs
248         this.datarecord = new openerp.base.DataRecord(this.session, this.model,{});
249         this.datarecord.on_loaded.add(this.on_record_loaded);
250
251     },
252     do_load_record: function() {
253         // if dataset is empty display the empty datarecord
254         if(this.dataset.ids.length == 0) {
255             this.on_record_loaded();
256         }
257         this.datarecord.load(this.dataset.ids[this.dataset_index]);
258     },
259     on_record_loaded: function() {
260         //for i in  this.fields: f.update_from_datarecord()
261     },
262 });
263
264 openerp.base.ListView = openerp.base.Controller.extend({
265     init: function(session, element_id, dataset, view_id) {
266         this._super(session, element_id);
267         this.dataset = dataset;
268         this.model = dataset.model;
269         this.view_id = view_id;
270         this.name = "";
271
272         this.cols = [];
273
274         this.$table = null;
275         this.colnames = [];
276         this.colmodel = [];
277
278         this.event_loading = false; // TODO in the future prevent abusive click by masking
279     },
280     start: function() {
281         //this.log('Starting ListView '+this.model+this.view_id)
282         this.rpc("/base/listview/load", {"model": this.model, "view_id":this.view_id}, this.on_loaded);
283     },
284     on_loaded: function(data) {
285         this.fields_view = data.fields_view;
286         //this.log(this.fields_view);
287         this.name = "" + this.fields_view.arch.attrs.string;
288         this.$element.html(QWeb.render("ListView", {"fields_view": this.fields_view}));
289         this.$table = this.$element.find("table");
290         this.cols = [];
291         this.colnames = [];
292         this.colmodel = [];
293         // TODO uss a object for each col, fill it with view and fallback to dataset.model_field
294         var tree = this.fields_view.arch.children;
295         for(var i = 0; i < tree.length; i++)  {
296             var col = tree[i];
297             if(col.tag == "field") {
298                 this.cols.push(col.attrs.name);
299                 this.colnames.push(col.attrs.name);
300                 this.colmodel.push({ name: col.attrs.name, index: col.attrs.name });
301             }
302         }
303         //this.log(this.cols);
304         this.dataset.fields = this.cols;
305         this.dataset.on_loaded.add_last(this.do_fill_table);
306     },
307     do_fill_table: function() {
308         //this.log("do_fill_table");
309         
310         var self = this;
311         //this.log(this.dataset.data);
312         var rows = [];
313         var ids = this.dataset.ids;
314         for(var i = 0; i < ids.length; i++)  {
315             // TODO very strange is sometimes non existing ? even as admin ? example ir.ui.menu
316             var row = this.dataset.values[ids[i]];
317             if(row)
318                 rows.push(row);
319 //            else
320 //              debugger;
321         }
322         //this.log(rows);
323         this.$table.jqGrid({
324             data: rows,
325             datatype: "local",
326             height: "100%",
327             rowNum: 100,
328             //rowList: [10,20,30],
329             colNames: this.colnames,
330             colModel: this.colmodel,
331             //pager: "#plist47",
332             viewrecords: true,
333             caption: this.name
334         }).setGridWidth(this.$element.width());
335         $(window).bind('resize', function() { self.$table.setGridWidth(self.$element.width()); }).trigger('resize');
336     }
337 });
338
339 openerp.base.TreeView = openerp.base.Controller.extend({
340 });
341
342 openerp.base.Widget = openerp.base.Controller.extend({
343     // TODO Change this to init: function(view, node) { and use view.session and a new element_id for the super
344     // it means that widgets are special controllers
345     init: function(session, element_id, view, node) {
346         var type = view.fields_view.fields[node.attrs.name] || {};
347         this.type = node.attrs.widget || type.type || node.tag;
348         this.name = node.attrs.name;
349         var type = view.fields_view.fields[node.attrs.name] || {};
350         this.type = node.attrs.widget || type.type || node.tag;
351         this.element_id = (node.tag == this.type ? node.tag : node.tag + '_' + this.type) + '_';
352         this.element_id += (this.name ? this.name + (this.is_field_label ? '_label' : '') + '_' : '');
353         this.element_id += Math.round(Math.random() * (new Date()).getTime());
354         this._super(session, this.element_id);
355         this.view = view;
356         this.view.widgets[this.element_id] = this;
357         this.node = node;
358         this.children = node.children;
359         this.colspan = parseInt(node.attrs.colspan || 1);
360         if (node.tag == 'field') {
361             this.view.fields[node.attrs.name] = this;
362             if (node.attrs.nolabel != '1' && this.colspan > 1) {
363                 this.colspan--;
364             }
365         }
366         this.field = view.fields_view.fields[node.attrs.name];
367         this.template = "FormView.widget";
368
369         this.invisible = (node.attrs.invisible == '1');
370         this.string = node.attrs.string || (this.field ? this.field.string : undefined);
371         this.help = node.attrs.help || (this.field ? this.field.help : undefined);
372         this.nolabel = (node.attrs.nolabel == '1');
373     },
374     register: function() {
375         this.$element = $('#' + this.element_id);
376         return this;
377     },
378     render: function() {
379         var template = this.template;
380         return QWeb.render(template, { "widget": this });
381     }
382 });
383
384 openerp.base.WidgetFrame = openerp.base.Widget.extend({
385     init: function(session, element_id, view, node) {
386         this._super(session, element_id, view, node);
387         this.template = "FormView.frame";
388         this.columns = node.attrs.col || 4;
389         this.x = 0;
390         this.y = 0;
391         this.table = [];
392         this.add_row();
393         for (var i = 0; i < node.children.length; i++) {
394             var n = node.children[i];
395             if (n.tag == "newline") {
396                 this.add_row();
397             } else {
398                 this.handle_node(n);
399             }
400         }
401         this.set_row_cells_with(this.table[this.table.length - 1]);
402     },
403     add_row: function(){
404         if (this.table.length) {
405             this.set_row_cells_with(this.table[this.table.length - 1]);
406         }
407         var row = [];
408         this.table.push(row);
409         this.x = 0;
410         this.y += 1;
411         return row;
412     },
413     set_row_cells_with: function(row) {
414         for (var i = 0; i < row.length; i++) {
415             var w = row[i];
416             if (w.is_field_label) {
417                 w.width = "1%";
418                 if (row[i + 1]) {
419                     row[i + 1].width = Math.round((100 / this.columns) * (w.colspan + 1) - 1) + '%';
420                 }
421             } else if (w.width === undefined) {
422                 w.width = Math.round((100 / this.columns) * w.colspan) + '%';
423             }
424         }
425     },
426     handle_node: function(n) {
427         var type = this.view.fields_view.fields[n.attrs.name] || {};
428         var widget_type = n.attrs.widget || type.type || n.tag;
429         if (openerp.base.widgets[widget_type]) {
430             var widget = new openerp.base.widgets[widget_type](this.session, null, this.view, n);
431             if (n.tag == 'field' && n.attrs.nolabel != '1') {
432                 var label = new openerp.base.widgets['label'](this.session, null, this.view, n);
433                 label["for"] = widget;
434                 this.add_widget(label);
435             }
436             this.add_widget(widget);
437         } else {
438             console.log("Unhandled widget type : " + widget_type, n);
439         }
440     },
441     add_widget: function(w) {
442         if (!w.invisible) {
443             var current_row = this.table[this.table.length - 1];
444             if (current_row.length && (this.x + w.colspan) > this.columns) {
445                 current_row = this.add_row();
446             }
447             current_row.push(w);
448             this.x += w.colspan;
449         }
450         return w;
451     }
452 });
453
454 openerp.base.WidgetNotebook = openerp.base.Widget.extend({
455     init: function(session, element_id, view, node) {
456         this._super(session, element_id, view, node);
457         this.template = "FormView.notebook";
458         this.pages = [];
459         for (var i = 0; i < node.children.length; i++) {
460             var n = node.children[i];
461             if (n.tag == "page") {
462                 var page = new openerp.base.WidgetFrame(this.session, null, this.view, n);
463                 this.pages.push(page);
464             }
465         }
466     }
467 });
468
469 openerp.base.WidgetSeparator = openerp.base.Widget.extend({
470     init: function(session, element_id, view, node) {
471         this._super(session, element_id, view, node);
472         this.template = "FormView.separator";
473     }
474 });
475
476 openerp.base.WidgetLabel = openerp.base.Widget.extend({
477     init: function(session, element_id, view, node) {
478         this.is_field_label = true;
479         this._super(session, element_id, view, node);
480         this.template = "FormView.label";
481         this.colspan = 1;
482     }
483 });
484
485 openerp.base.WidgetButton = openerp.base.Widget.extend({
486     init: function(session, element_id, view, node) {
487         this._super(session, element_id, view, node);
488         this.template = "FormView.button";
489     }
490 });
491
492 openerp.base.Field = openerp.base.Widget.extend({
493     init: function(session, element_id, view, node) {
494         this._super(session, element_id, view, node);
495         // this.datarecord = this.view.datarecord ??
496     }
497 });
498
499 openerp.base.FieldChar = openerp.base.Field.extend({
500     init: function(session, element_id, view, node) {
501         this._super(session, element_id, view, node);
502         this.template = "FormView.field.char";
503
504     },
505     start: function() {
506         // this.$element.bind('leaving_focus',)
507     },
508     set_value: function() {
509         // this.$element.val(this.view.datarecord.values[this.name])
510     },
511     on_change: function() {
512         //this.view.update_field(this.name,value);
513
514     },
515 });
516
517 openerp.base.FieldEmail = openerp.base.Field.extend({
518     init: function(session, element_id, view, node) {
519         this._super(session, element_id, view, node);
520         this.template = "FormView.field.char";
521     }
522 });
523
524 openerp.base.FieldFloat = openerp.base.Field.extend({
525     init: function(session, element_id, view, node) {
526         this._super(session, element_id, view, node);
527         this.template = "FormView.field.char";
528     }
529 });
530
531 openerp.base.FieldBoolean = openerp.base.Field.extend({
532     init: function(session, element_id, view, node) {
533         this._super(session, element_id, view, node);
534         this.template = "FormView.field.boolean";
535     }
536 });
537
538 openerp.base.FieldDate = openerp.base.Field.extend({
539     init: function(session, element_id, view, node) {
540         this._super(session, element_id, view, node);
541         this.template = "FormView.field.date";
542     }
543 });
544
545 openerp.base.FieldDatetime = openerp.base.Field.extend({
546     init: function(session, element_id, view, node) {
547         this._super(session, element_id, view, node);
548         this.template = "FormView.field.datetime";
549     }
550 });
551
552 openerp.base.FieldText = openerp.base.Field.extend({
553     init: function(session, element_id, view, node) {
554         this._super(session, element_id, view, node);
555         this.template = "FormView.field.text";
556     }
557 });
558
559 openerp.base.FieldTextXml = openerp.base.Field.extend({
560 // to replace view editor
561 });
562
563 openerp.base.FieldSelection = openerp.base.Field.extend({
564     init: function(session, element_id, view, node) {
565         this._super(session, element_id, view, node);
566         this.template = "FormView.field.selection";
567     }
568 });
569
570 openerp.base.FieldMany2One = openerp.base.Field.extend({
571     init: function(session, element_id, view, node) {
572         this._super(session, element_id, view, node);
573         this.template = "FormView.field.many2one";
574     }
575 });
576
577 openerp.base.FieldOne2Many = openerp.base.Field.extend({
578     init: function(session, element_id, view, node) {
579         this._super(session, element_id, view, node);
580         this.template = "FormView.field.one2many";
581     }
582 });
583
584 openerp.base.FieldMany2Many = openerp.base.Field.extend({
585     init: function(session, element_id, view, node) {
586         this._super(session, element_id, view, node);
587         this.template = "FormView.field.one2many";
588     }
589 });
590
591 openerp.base.FieldReference = openerp.base.Field.extend({
592     init: function(session, element_id, view, node) {
593         this._super(session, element_id, view, node);
594         this.template = "FormView.field.reference";
595     }
596 });
597
598 openerp.base.widgets = {
599     'group' : openerp.base.WidgetFrame,
600     'notebook' : openerp.base.WidgetNotebook,
601     'separator' : openerp.base.WidgetSeparator,
602     'label' : openerp.base.WidgetLabel,
603     'char' : openerp.base.FieldChar,
604     'email' : openerp.base.FieldEmail,
605     'date' : openerp.base.FieldDate,
606     'datetime' : openerp.base.FieldDatetime,
607     'text' : openerp.base.FieldText,
608     'selection' : openerp.base.FieldSelection,
609     'many2one' : openerp.base.FieldMany2One,
610     'one2many' : openerp.base.FieldOne2Many,
611     'reference' : openerp.base.FieldReference,
612     'boolean' : openerp.base.FieldBoolean,
613     'float' : openerp.base.FieldFloat,
614     'button' : openerp.base.WidgetButton
615 }
616
617 openerp.base.CalendarView = openerp.base.Controller.extend({
618 // Dhtmlx scheduler ?
619 });
620
621 openerp.base.GanttView = openerp.base.Controller.extend({
622 // Dhtmlx gantt ?
623 });
624
625 openerp.base.DiagramView = openerp.base.Controller.extend({
626 // 
627 });
628
629 openerp.base.GraphView = openerp.base.Controller.extend({
630 });
631
632 openerp.base.ProcessView = openerp.base.Controller.extend({
633 });
634
635 openerp.base.HelpView = openerp.base.Controller.extend({
636 });
637
638 };
639
640 // DEBUG_RPC:rpc.request:('execute', 'addons-dsh-l10n_us', 1, '*', ('ir.filters', 'get_filters', u'res.partner'))
641 // vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax: