]> git.pond.sub.org Git - eow/blob - static/dojo-release-1.1.1/dojox/grid/VirtualGrid.js
add Dojo 1.1.1
[eow] / static / dojo-release-1.1.1 / dojox / grid / VirtualGrid.js
1 if(!dojo._hasResource["dojox.grid.VirtualGrid"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2 dojo._hasResource["dojox.grid.VirtualGrid"] = true;
3 dojo.provide("dojox.grid.VirtualGrid");
4
5 dojo.require("dojox.grid._grid.lib");
6 dojo.require("dojox.grid._grid.scroller");
7 dojo.require("dojox.grid._grid.view");
8 dojo.require("dojox.grid._grid.views");
9 dojo.require("dojox.grid._grid.layout");
10 dojo.require("dojox.grid._grid.rows");
11 dojo.require("dojox.grid._grid.focus");
12 dojo.require("dojox.grid._grid.selection");
13 dojo.require("dojox.grid._grid.edit");
14 dojo.require("dojox.grid._grid.rowbar");
15 dojo.require("dojox.grid._grid.publicEvents");
16
17 dojo.declare('dojox.VirtualGrid', 
18         [ dijit._Widget, dijit._Templated ], 
19         {
20         // summary:
21         //              A grid widget with virtual scrolling, cell editing, complex rows,
22         //              sorting, fixed columns, sizeable columns, etc.
23         //
24         //      description:
25         //              VirtualGrid provides the full set of grid features without any
26         //              direct connection to a data store.
27         //
28         //              The grid exposes a get function for the grid, or optionally
29         //              individual columns, to populate cell contents.
30         //
31         //              The grid is rendered based on its structure, an object describing
32         //              column and cell layout.
33         //
34         //      example:
35         //              A quick sample:
36         //              
37         //              define a get function
38         //      |       function get(inRowIndex){ // called in cell context
39         //      |               return [this.index, inRowIndex].join(', ');
40         //      |       }
41         //              
42         //              define the grid structure:
43         //      |       var structure = [ // array of view objects
44         //      |               { cells: [// array of rows, a row is an array of cells
45         //      |                       [
46         //      |                               { name: "Alpha", width: 6 }, 
47         //      |                               { name: "Beta" }, 
48         //      |                               { name: "Gamma", get: get }]
49         //      |               ]}
50         //      |       ];
51         //              
52         //      |       <div id="grid" 
53         //      |               rowCount="100" get="get" 
54         //      |               structure="structure" 
55         //      |               dojoType="dojox.VirtualGrid"></div>
56
57         templateString:"<div class=\"dojoxGrid\" hidefocus=\"hidefocus\" role=\"wairole:grid\">\n\t<div class=\"dojoxGrid-master-header\" dojoAttachPoint=\"viewsHeaderNode\"></div>\n\t<div class=\"dojoxGrid-master-view\" dojoAttachPoint=\"viewsNode\"></div>\n\t<span dojoAttachPoint=\"lastFocusNode\" tabindex=\"0\"></span>\n</div>\n",
58         
59         // classTag: String
60         //              CSS class applied to the grid's domNode
61         classTag: 'dojoxGrid',
62
63         get: function(inRowIndex){
64                 // summary: Default data getter. 
65                 // description:
66                 //              Provides data to display in a grid cell. Called in grid cell context.
67                 //              So this.cell.index is the column index.
68                 // inRowIndex: Integer
69                 //              Row for which to provide data
70                 // returns:
71                 //              Data to display for a given grid cell.
72         },
73         
74         // settings
75         // rowCount: Integer
76         //              Number of rows to display. 
77         rowCount: 5,
78
79         // keepRows: Integer
80         //              Number of rows to keep in the rendering cache.
81         keepRows: 75,
82         
83         // rowsPerPage: Integer
84         //              Number of rows to render at a time.
85         rowsPerPage: 25,
86
87         // autoWidth: Boolean
88         //              If autoWidth is true, grid width is automatically set to fit the data.
89         autoWidth: false,
90         
91         // autoHeight: Boolean
92         //              If autoHeight is true, grid height is automatically set to fit the data.
93         autoHeight: false,
94         
95         // autoRender: Boolean
96         //              If autoRender is true, grid will render itself after initialization.
97         autoRender: true,
98
99         // defaultHeight: String
100         //              default height of the grid, measured in any valid css unit.
101         defaultHeight: '15em',
102
103         // structure: Object|String
104         //              View layout defintion. Can be set to a layout object, or to the (string) name of a layout object.
105         structure: '',
106
107         // elasticView: Integer
108         //      Override defaults and make the indexed grid view elastic, thus filling available horizontal space.
109         elasticView: -1,
110         
111         // singleClickEdit: boolean
112         //              Single-click starts editing. Default is double-click
113         singleClickEdit: false,
114
115         // Used to store the last two clicks, to ensure double-clicking occurs based on the intended row
116         _click: null,
117         
118         // private
119         sortInfo: 0,
120         themeable: true,
121
122         // initialization
123         buildRendering: function(){
124                 this.inherited(arguments);
125                 // reset get from blank function (needed for markup parsing) to null, if not changed
126                 if(this.get == dojox.VirtualGrid.prototype.get){
127                         this.get = null;
128                 }
129                 if(!this.domNode.getAttribute('tabIndex')){
130                         this.domNode.tabIndex = "0";
131                 }
132                 this.createScroller();
133                 this.createLayout();
134                 this.createViews();
135                 this.createManagers();
136                 dojox.grid.initTextSizePoll();
137                 this.connect(dojox.grid, "textSizeChanged", "textSizeChanged");
138                 dojox.grid.funnelEvents(this.domNode, this, 'doKeyEvent', dojox.grid.keyEvents);
139                 this.connect(this, "onShow", "renderOnIdle");
140         },
141         postCreate: function(){
142                 // replace stock styleChanged with one that triggers an update
143                 this.styleChanged = this._styleChanged;
144                 this.setStructure(this.structure);
145                 this._click = [];
146         },
147         
148         destroy: function(){
149                 this.domNode.onReveal = null;
150                 this.domNode.onSizeChange = null;
151                 this.edit.destroy();
152                 this.views.destroyViews();
153                 this.inherited(arguments);
154         },
155         
156         styleChanged: function(){
157                 this.setStyledClass(this.domNode, '');
158         },
159         
160         _styleChanged: function(){
161                 this.styleChanged();
162                 this.update();
163         },
164         
165         textSizeChanged: function(){
166                 setTimeout(dojo.hitch(this, "_textSizeChanged"), 1);
167         },
168         
169         _textSizeChanged: function(){
170                 if(this.domNode){
171                         this.views.forEach(function(v){
172                                 v.content.update();
173                         });
174                         this.render();
175                 }
176         },
177         
178         sizeChange: function(){
179                 dojox.grid.jobs.job(this.id + 'SizeChange', 50, dojo.hitch(this, "update"));
180         },
181         
182         renderOnIdle: function() {
183                 setTimeout(dojo.hitch(this, "render"), 1);
184         },
185         
186         createManagers: function(){
187                 // summary:
188                 //              create grid managers for various tasks including rows, focus, selection, editing
189                 
190                 // row manager
191                 this.rows = new dojox.grid.rows(this);
192                 // focus manager
193                 this.focus = new dojox.grid.focus(this);
194                 // selection manager
195                 this.selection = new dojox.grid.selection(this);
196                 // edit manager
197                 this.edit = new dojox.grid.edit(this);
198         },
199
200         createScroller: function(){
201                 // summary: Creates a new virtual scroller
202                 this.scroller = new dojox.grid.scroller.columns();
203                 this.scroller._pageIdPrefix = this.id + '-';
204                 this.scroller.renderRow = dojo.hitch(this, "renderRow");
205                 this.scroller.removeRow = dojo.hitch(this, "rowRemoved");
206         },
207
208         createLayout: function(){
209                 // summary: Creates a new Grid layout
210                 this.layout = new dojox.grid.layout(this);
211         },
212
213         // views
214         createViews: function(){
215                 this.views = new dojox.grid.views(this);
216                 this.views.createView = dojo.hitch(this, "createView");
217         },
218         
219         createView: function(inClass){
220                 if(dojo.isAIR){
221                         var obj = window;
222                         var names = inClass.split('.');
223                         for(var i=0;i<names.length;i++){
224                                 if(typeof obj[names[i]]=='undefined'){
225                                         var undefstring = names[0];
226                                         for(var j=1;j<=i;j++){
227                                                 undefstring+="."+names[j];
228                                         }
229                                         throw new Error(undefstring+" is undefined");
230                                 }
231                                 obj = obj[names[i]];
232                         }
233                         var c = obj;
234                 }else{
235                         var c = eval(inClass);
236                 }
237                 var view = new c({ grid: this });
238                 this.viewsNode.appendChild(view.domNode);
239                 this.viewsHeaderNode.appendChild(view.headerNode);
240                 this.views.addView(view);
241                 return view;
242         },
243
244         buildViews: function(){
245                 for(var i=0, vs; (vs=this.layout.structure[i]); i++){
246                         this.createView(vs.type || dojox._scopeName + ".GridView").setStructure(vs);
247                 }
248                 this.scroller.setContentNodes(this.views.getContentNodes());
249         },
250         
251         setStructure: function(inStructure){
252                 // summary:
253                 //              Install a new structure and rebuild the grid.
254                 // inStructure: Object
255                 //              Structure object defines the grid layout and provides various
256                 //              options for grid views and columns
257                 //      description:
258                 //              A grid structure is an array of view objects. A view object can
259                 //              specify a view type (view class), width, noscroll (boolean flag
260                 //              for view scrolling), and cells. Cells is an array of objects
261                 //              corresponding to each grid column. The view cells object is an
262                 //              array of subrows comprising a single row. Each subrow is an
263                 //              array of column objects. A column object can have a name,
264                 //              width, value (default), get function to provide data, styles,
265                 //              and span attributes (rowSpan, colSpan).
266
267                 this.views.destroyViews();
268                 this.structure = inStructure;
269                 if((this.structure)&&(dojo.isString(this.structure))){
270                         this.structure=dojox.grid.getProp(this.structure);
271                 }
272                 if(!this.structure){
273                         this.structure=window["layout"];
274                 }
275                 if(!this.structure){
276                         return;
277                 }
278                 this.layout.setStructure(this.structure);
279                 this._structureChanged();
280         },
281
282         _structureChanged: function() {
283                 this.buildViews();
284                 if(this.autoRender){
285                         this.render();
286                 }
287         },
288
289         hasLayout: function() {
290                 return this.layout.cells.length;
291         },
292
293         // sizing
294         resize: function(sizeBox){
295         // summary:
296                 //              Update the grid's rendering dimensions and resize it
297                 // sizeBox: Object?
298                 //              {w: int, h: int, l: int, t: int}
299                 
300                 // FIXME: If grid is not sized explicitly, sometimes bogus scrollbars 
301                 // can appear in our container, which may require an extra call to 'resize'
302                 // to sort out.
303                 this._sizeBox = sizeBox;
304                 this._resize();
305                 this.sizeChange();
306         },
307         
308         _getPadBorder: function() {
309                 this._padBorder = this._padBorder || dojo._getPadBorderExtents(this.domNode);
310                 return this._padBorder;
311         },
312         
313         _resize: function(){
314                 // if we have set up everything except the DOM, we cannot resize
315                 if(!this.domNode.parentNode || this.domNode.parentNode.nodeType != 1 || !this.hasLayout()){
316                         return;
317                 }
318                 // useful measurement
319                 var padBorder = this._getPadBorder();
320                 // grid height
321                 if(this.autoHeight){
322                         this.domNode.style.height = 'auto';
323                         this.viewsNode.style.height = '';
324                 }else if(this.flex > 0){
325                 }else if(this.domNode.clientHeight <= padBorder.h){
326                         if(this.domNode.parentNode == document.body){
327                                 this.domNode.style.height = this.defaultHeight;
328                         }else{
329                                 this.fitTo = "parent";
330                         }
331                 }
332                 // if we are given dimensions, size the grid's domNode to those dimensions
333                 if(this._sizeBox){
334                         dojo.contentBox(this.domNode, this._sizeBox);
335                 }else if(this.fitTo == "parent"){
336                         var h = dojo._getContentBox(this.domNode.parentNode).h;
337                         dojo.marginBox(this.domNode, { h: Math.max(0, h) });
338                 }
339                 
340                 var h = dojo._getContentBox(this.domNode).h;
341                 if(h == 0 && !this.autoHeight){
342                         // We need to hide the header, since the Grid is essentially hidden.
343                         this.viewsHeaderNode.style.display = "none";
344                 }else{
345                         // Otherwise, show the header and give it an appropriate height.
346                         this.viewsHeaderNode.style.display = "block";
347                 }
348                 
349                 // NOTE: it is essential that width be applied before height
350                 // Header height can only be calculated properly after view widths have been set.
351                 // This is because flex column width is naturally 0 in Firefox.
352                 // Therefore prior to width sizing flex columns with spaces are maximally wrapped 
353                 // and calculated to be too tall.
354                 this.adaptWidth();
355                 this.adaptHeight();
356                 
357                 // default row height (FIXME: use running average(?), remove magic #)
358                 this.scroller.defaultRowHeight = this.rows.getDefaultHeightPx() + 1;
359                 this.postresize();
360         },
361
362         adaptWidth: function() {
363                 // private: sets width and position for views and update grid width if necessary
364                 var
365                         w = this.autoWidth ? 0 : this.domNode.clientWidth || (this.domNode.offsetWidth - this._getPadBorder().w);
366                         vw = this.views.arrange(1, w);
367                 this.views.onEach("adaptWidth");
368                 if (this.autoWidth)
369                         this.domNode.style.width = vw + "px";
370         },
371
372         adaptHeight: function(){
373                 // private: measures and normalizes header height, then sets view heights, and then updates scroller
374                 var vns = this.viewsHeaderNode.style, t = vns.display == "none" ? 0 : this.views.measureHeader();
375                 vns.height = t + 'px';
376                 // header heights are reset during measuring so must be normalized after measuring.
377                 this.views.normalizeHeaderNodeHeight();
378                 // content extent
379                 var h = (this.autoHeight ? -1 : Math.max(this.domNode.clientHeight - t, 0) || 0);
380                 this.views.onEach('setSize', [0, h]);
381                 this.views.onEach('adaptHeight');
382                 this.scroller.windowHeight = h; 
383         },
384
385         // render 
386         render: function(){
387                 // summary:
388                 //      Render the grid, headers, and views. Edit and scrolling states are reset. To retain edit and 
389                 // scrolling states, see Update.
390
391                 if(!this.domNode){return;}
392                 
393                 if(!this.hasLayout()) {
394                         this.scroller.init(0, this.keepRows, this.rowsPerPage);
395                         return;
396                 }
397                 //
398                 this.update = this.defaultUpdate;
399                 this.scroller.init(this.rowCount, this.keepRows, this.rowsPerPage);
400                 this.prerender();
401                 this.setScrollTop(0);
402                 this.postrender();
403         },
404
405         prerender: function(){
406                 // if autoHeight, make sure scroller knows not to virtualize; everything must be rendered.
407                 this.keepRows = this.autoHeight ? 0 : this.constructor.prototype.keepRows;
408                 this.scroller.setKeepInfo(this.keepRows);
409                 this.views.render();
410                 this._resize();
411         },
412
413         postrender: function(){
414                 this.postresize();
415                 this.focus.initFocusView();
416                 // make rows unselectable
417                 dojo.setSelectable(this.domNode, false);
418         },
419
420         postresize: function(){
421                 // views are position absolute, so they do not inflate the parent
422                 if(this.autoHeight){
423                         this.viewsNode.style.height = this.views.measureContent() + 'px';
424                 }
425         },
426
427         renderRow: function(inRowIndex, inNodes){
428                 // summary: private, used internally to render rows
429                 this.views.renderRow(inRowIndex, inNodes);
430         },
431
432         rowRemoved: function(inRowIndex){
433                 // summary: private, used internally to remove rows
434                 this.views.rowRemoved(inRowIndex);
435         },
436
437         invalidated: null,
438
439         updating: false,
440
441         beginUpdate: function(){
442                 // summary:
443                 //              Use to make multiple changes to rows while queueing row updating.
444                 // NOTE: not currently supporting nested begin/endUpdate calls
445                 this.invalidated = [];
446                 this.updating = true;
447         },
448
449         endUpdate: function(){
450                 // summary:
451                 //              Use after calling beginUpdate to render any changes made to rows.
452                 this.updating = false;
453                 var i = this.invalidated;
454                 if(i.all){
455                         this.update();
456                 }else if(i.rowCount != undefined){
457                         this.updateRowCount(i.rowCount);
458                 }else{
459                         for(r in i){
460                                 this.updateRow(Number(r));
461                         }
462                 }
463                 this.invalidated = null;
464         },
465
466         // update
467         defaultUpdate: function(){
468                 // note: initial update calls render and subsequently this function.
469                 if(!this.domNode){return;}
470                 if(this.updating){
471                         this.invalidated.all = true;
472                         return;
473                 }
474                 //this.edit.saveState(inRowIndex);
475                 this.prerender();
476                 this.scroller.invalidateNodes();
477                 this.setScrollTop(this.scrollTop);
478                 this.postrender();
479                 //this.edit.restoreState(inRowIndex);
480         },
481
482         update: function(){
483                 // summary:
484                 //              Update the grid, retaining edit and scrolling states.
485                 this.render();
486         },
487
488         updateRow: function(inRowIndex){
489                 // summary:
490                 //              Render a single row.
491                 // inRowIndex: Integer
492                 //              Index of the row to render
493                 inRowIndex = Number(inRowIndex);
494                 if(this.updating){
495                         this.invalidated[inRowIndex]=true;
496                 }else{
497                         this.views.updateRow(inRowIndex, this.rows.getHeight(inRowIndex));
498                         this.scroller.rowHeightChanged(inRowIndex);
499                 }
500         },
501
502         updateRowCount: function(inRowCount){
503                 //summary: 
504                 //      Change the number of rows.
505                 // inRowCount: int
506                 //      Number of rows in the grid.
507                 if(this.updating){
508                         this.invalidated.rowCount = inRowCount;
509                 }else{
510                         this.rowCount = inRowCount;
511                         if(this.layout.cells.length){
512                                 this.scroller.updateRowCount(inRowCount);
513                                 this.setScrollTop(this.scrollTop);
514                         }
515                         this._resize();
516                 }
517         },
518
519         updateRowStyles: function(inRowIndex){
520                 // summary:
521                 //              Update the styles for a row after it's state has changed.
522                 this.views.updateRowStyles(inRowIndex);
523         },
524
525         rowHeightChanged: function(inRowIndex){
526                 // summary: 
527                 //              Update grid when the height of a row has changed. Row height is handled automatically as rows
528                 //              are rendered. Use this function only to update a row's height outside the normal rendering process.
529                 // inRowIndex: Integer
530                 //              index of the row that has changed height
531                 
532                 this.views.renormalizeRow(inRowIndex);
533                 this.scroller.rowHeightChanged(inRowIndex);
534         },
535         
536         // fastScroll: Boolean
537         //              flag modifies vertical scrolling behavior. Defaults to true but set to false for slower 
538         //              scroll performance but more immediate scrolling feedback
539         fastScroll: true,
540         
541         delayScroll: false,
542
543         // scrollRedrawThreshold: int
544         //      pixel distance a user must scroll vertically to trigger grid scrolling.
545         scrollRedrawThreshold: (dojo.isIE ? 100 : 50),
546
547         // scroll methods
548         scrollTo: function(inTop){
549                 // summary:
550                 //              Vertically scroll the grid to a given pixel position
551                 // inTop: Integer
552                 //              vertical position of the grid in pixels
553                 if(!this.fastScroll){
554                         this.setScrollTop(inTop);
555                         return;
556                 }
557                 var delta = Math.abs(this.lastScrollTop - inTop);
558                 this.lastScrollTop = inTop;
559                 if(delta > this.scrollRedrawThreshold || this.delayScroll){
560                         this.delayScroll = true;
561                         this.scrollTop = inTop;
562                         this.views.setScrollTop(inTop);
563                         dojox.grid.jobs.job('dojoxGrid-scroll', 200, dojo.hitch(this, "finishScrollJob"));
564                 }else{
565                         this.setScrollTop(inTop);
566                 }
567         },
568         
569         finishScrollJob: function(){
570                 this.delayScroll = false;
571                 this.setScrollTop(this.scrollTop);
572         },
573         
574         setScrollTop: function(inTop){
575                 this.scrollTop = this.views.setScrollTop(inTop);
576                 this.scroller.scroll(this.scrollTop);
577         },
578         
579         scrollToRow: function(inRowIndex){
580                 // summary:
581                 //              Scroll the grid to a specific row.
582                 // inRowIndex: Integer
583                 //              grid row index
584                 this.setScrollTop(this.scroller.findScrollTop(inRowIndex) + 1);
585         },
586         
587         // styling (private, used internally to style individual parts of a row)
588         styleRowNode: function(inRowIndex, inRowNode){
589                 if(inRowNode){
590                         this.rows.styleRowNode(inRowIndex, inRowNode);
591                 }
592         },
593
594         // cells
595         getCell: function(inIndex){
596                 // summary:
597                 //              Retrieves the cell object for a given grid column.
598                 // inIndex: Integer
599                 //              Grid column index of cell to retrieve
600                 // returns:
601                 //              a grid cell
602                 return this.layout.cells[inIndex];
603         },
604
605         setCellWidth: function(inIndex, inUnitWidth) {
606                 this.getCell(inIndex).unitWidth = inUnitWidth;
607         },
608
609         getCellName: function(inCell){
610                 // summary: Returns the cell name of a passed cell
611                 return "Cell " + inCell.index; // String
612         },
613
614         // sorting
615         canSort: function(inSortInfo){
616                 // summary:
617                 //              Determines if the grid can be sorted
618                 // inSortInfo: Integer
619                 //              Sort information, 1-based index of column on which to sort, positive for an ascending sort
620                 //              and negative for a descending sort
621                 // returns: Boolean
622                 //              True if grid can be sorted on the given column in the given direction
623         },
624         
625         sort: function(){
626         },
627         
628         getSortAsc: function(inSortInfo){
629                 // summary:
630                 //              Returns true if grid is sorted in an ascending direction.
631                 inSortInfo = inSortInfo == undefined ? this.sortInfo : inSortInfo;
632                 return Boolean(inSortInfo > 0); // Boolean
633         },
634         
635         getSortIndex: function(inSortInfo){
636                 // summary:
637                 //              Returns the index of the column on which the grid is sorted
638                 inSortInfo = inSortInfo == undefined ? this.sortInfo : inSortInfo;
639                 return Math.abs(inSortInfo) - 1; // Integer
640         },
641         
642         setSortIndex: function(inIndex, inAsc){
643                 // summary:
644                 //              Sort the grid on a column in a specified direction
645                 // inIndex: Integer
646                 //              Column index on which to sort.
647                 // inAsc: Boolean
648                 //              If true, sort the grid in ascending order, otherwise in descending order
649                 var si = inIndex +1;
650                 if(inAsc != undefined){
651                         si *= (inAsc ? 1 : -1);
652                 } else if(this.getSortIndex() == inIndex){
653                         si = -this.sortInfo;
654                 }
655                 this.setSortInfo(si);
656         },
657         
658         setSortInfo: function(inSortInfo){
659                 if(this.canSort(inSortInfo)){
660                         this.sortInfo = inSortInfo;
661                         this.sort();
662                         this.update();
663                 }
664         },
665         
666         // DOM event handler
667         doKeyEvent: function(e){
668                 e.dispatch = 'do' + e.type;
669                 this.onKeyEvent(e);
670         },
671
672         // event dispatch
673         //: protected
674         _dispatch: function(m, e){
675                 if(m in this){
676                         return this[m](e);
677                 }
678         },
679
680         dispatchKeyEvent: function(e){
681                 this._dispatch(e.dispatch, e);
682         },
683         
684         dispatchContentEvent: function(e){
685                 this.edit.dispatchEvent(e) || e.sourceView.dispatchContentEvent(e) || this._dispatch(e.dispatch, e);
686         },
687         
688         dispatchHeaderEvent: function(e){
689                 e.sourceView.dispatchHeaderEvent(e) || this._dispatch('doheader' + e.type, e);
690         },
691         
692         dokeydown: function(e){
693                 this.onKeyDown(e);
694         },
695         
696         doclick: function(e){
697                 if(e.cellNode){
698                         this.onCellClick(e);
699                 }else{
700                         this.onRowClick(e);
701                 }
702         },
703         
704         dodblclick: function(e){
705                 if(e.cellNode){
706                         this.onCellDblClick(e);
707                 }else{
708                         this.onRowDblClick(e);
709                 }
710         },
711         
712         docontextmenu: function(e){
713                 if(e.cellNode){
714                         this.onCellContextMenu(e);
715                 }else{
716                         this.onRowContextMenu(e);
717                 }
718         },
719         
720         doheaderclick: function(e){
721                 if(e.cellNode){
722                         this.onHeaderCellClick(e);
723                 }else{
724                         this.onHeaderClick(e);
725                 }
726         },
727         
728         doheaderdblclick: function(e){
729                 if(e.cellNode){
730                         this.onHeaderCellDblClick(e);
731                 }else{
732                         this.onHeaderDblClick(e);
733                 }
734         },
735         
736         doheadercontextmenu: function(e){
737                 if(e.cellNode){
738                         this.onHeaderCellContextMenu(e);
739                 }else{
740                         this.onHeaderContextMenu(e);
741                 }
742         },
743         
744         // override to modify editing process
745         doStartEdit: function(inCell, inRowIndex){
746                 this.onStartEdit(inCell, inRowIndex);
747         },
748         
749         doApplyCellEdit: function(inValue, inRowIndex, inFieldIndex){
750                 this.onApplyCellEdit(inValue, inRowIndex, inFieldIndex);
751         },
752         
753         doCancelEdit: function(inRowIndex){
754                 this.onCancelEdit(inRowIndex);
755         },
756         
757         doApplyEdit: function(inRowIndex){
758                 this.onApplyEdit(inRowIndex);
759         },
760         
761         // row editing
762         addRow: function(){
763                 // summary:
764                 //              Add a row to the grid.
765                 this.updateRowCount(this.rowCount+1);
766         },
767         
768         removeSelectedRows: function(){
769                 // summary:
770                 //              Remove the selected rows from the grid.
771                 this.updateRowCount(Math.max(0, this.rowCount - this.selection.getSelected().length));
772                 this.selection.clear();
773         }
774
775 });
776
777 dojo.mixin(dojox.VirtualGrid.prototype, dojox.grid.publicEvents);
778
779 }