Forked mumble-django project from https://bitbucket.org/Svedrin/mumble-django
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

5265 lines
178 KiB

  1. /*!
  2. * Ext JS Library 3.2.0
  3. * Copyright(c) 2006-2010 Ext JS, Inc.
  4. * licensing@extjs.com
  5. * http://www.extjs.com/license
  6. */
  7. /**
  8. * @class Ext.grid.GridPanel
  9. * @extends Ext.Panel
  10. * <p>This class represents the primary interface of a component based grid control to represent data
  11. * in a tabular format of rows and columns. The GridPanel is composed of the following:</p>
  12. * <div class="mdetail-params"><ul>
  13. * <li><b>{@link Ext.data.Store Store}</b> : The Model holding the data records (rows)
  14. * <div class="sub-desc"></div></li>
  15. * <li><b>{@link Ext.grid.ColumnModel Column model}</b> : Column makeup
  16. * <div class="sub-desc"></div></li>
  17. * <li><b>{@link Ext.grid.GridView View}</b> : Encapsulates the user interface
  18. * <div class="sub-desc"></div></li>
  19. * <li><b>{@link Ext.grid.AbstractSelectionModel selection model}</b> : Selection behavior
  20. * <div class="sub-desc"></div></li>
  21. * </ul></div>
  22. * <p>Example usage:</p>
  23. * <pre><code>
  24. var grid = new Ext.grid.GridPanel({
  25. {@link #store}: new {@link Ext.data.Store}({
  26. {@link Ext.data.Store#autoDestroy autoDestroy}: true,
  27. {@link Ext.data.Store#reader reader}: reader,
  28. {@link Ext.data.Store#data data}: xg.dummyData
  29. }),
  30. {@link #colModel}: new {@link Ext.grid.ColumnModel}({
  31. {@link Ext.grid.ColumnModel#defaults defaults}: {
  32. width: 120,
  33. sortable: true
  34. },
  35. {@link Ext.grid.ColumnModel#columns columns}: [
  36. {id: 'company', header: 'Company', width: 200, sortable: true, dataIndex: 'company'},
  37. {header: 'Price', renderer: Ext.util.Format.usMoney, dataIndex: 'price'},
  38. {header: 'Change', dataIndex: 'change'},
  39. {header: '% Change', dataIndex: 'pctChange'},
  40. // instead of specifying renderer: Ext.util.Format.dateRenderer('m/d/Y') use xtype
  41. {
  42. header: 'Last Updated', width: 135, dataIndex: 'lastChange',
  43. xtype: 'datecolumn', format: 'M d, Y'
  44. }
  45. ],
  46. }),
  47. {@link #viewConfig}: {
  48. {@link Ext.grid.GridView#forceFit forceFit}: true,
  49. // Return CSS class to apply to rows depending upon data values
  50. {@link Ext.grid.GridView#getRowClass getRowClass}: function(record, index) {
  51. var c = record.{@link Ext.data.Record#get get}('change');
  52. if (c < 0) {
  53. return 'price-fall';
  54. } else if (c > 0) {
  55. return 'price-rise';
  56. }
  57. }
  58. },
  59. {@link #sm}: new Ext.grid.RowSelectionModel({singleSelect:true}),
  60. width: 600,
  61. height: 300,
  62. frame: true,
  63. title: 'Framed with Row Selection and Horizontal Scrolling',
  64. iconCls: 'icon-grid'
  65. });
  66. * </code></pre>
  67. * <p><b><u>Notes:</u></b></p>
  68. * <div class="mdetail-params"><ul>
  69. * <li>Although this class inherits many configuration options from base classes, some of them
  70. * (such as autoScroll, autoWidth, layout, items, etc) are not used by this class, and will
  71. * have no effect.</li>
  72. * <li>A grid <b>requires</b> a width in which to scroll its columns, and a height in which to
  73. * scroll its rows. These dimensions can either be set explicitly through the
  74. * <tt>{@link Ext.BoxComponent#height height}</tt> and <tt>{@link Ext.BoxComponent#width width}</tt>
  75. * configuration options or implicitly set by using the grid as a child item of a
  76. * {@link Ext.Container Container} which will have a {@link Ext.Container#layout layout manager}
  77. * provide the sizing of its child items (for example the Container of the Grid may specify
  78. * <tt>{@link Ext.Container#layout layout}:'fit'</tt>).</li>
  79. * <li>To access the data in a Grid, it is necessary to use the data model encapsulated
  80. * by the {@link #store Store}. See the {@link #cellclick} event for more details.</li>
  81. * </ul></div>
  82. * @constructor
  83. * @param {Object} config The config object
  84. * @xtype grid
  85. */
  86. Ext.grid.GridPanel = Ext.extend(Ext.Panel, {
  87. /**
  88. * @cfg {String} autoExpandColumn
  89. * <p>The <tt>{@link Ext.grid.Column#id id}</tt> of a {@link Ext.grid.Column column} in
  90. * this grid that should expand to fill unused space. This value specified here can not
  91. * be <tt>0</tt>.</p>
  92. * <br><p><b>Note</b>: If the Grid's {@link Ext.grid.GridView view} is configured with
  93. * <tt>{@link Ext.grid.GridView#forceFit forceFit}=true</tt> the <tt>autoExpandColumn</tt>
  94. * is ignored. See {@link Ext.grid.Column}.<tt>{@link Ext.grid.Column#width width}</tt>
  95. * for additional details.</p>
  96. * <p>See <tt>{@link #autoExpandMax}</tt> and <tt>{@link #autoExpandMin}</tt> also.</p>
  97. */
  98. autoExpandColumn : false,
  99. /**
  100. * @cfg {Number} autoExpandMax The maximum width the <tt>{@link #autoExpandColumn}</tt>
  101. * can have (if enabled). Defaults to <tt>1000</tt>.
  102. */
  103. autoExpandMax : 1000,
  104. /**
  105. * @cfg {Number} autoExpandMin The minimum width the <tt>{@link #autoExpandColumn}</tt>
  106. * can have (if enabled). Defaults to <tt>50</tt>.
  107. */
  108. autoExpandMin : 50,
  109. /**
  110. * @cfg {Boolean} columnLines <tt>true</tt> to add css for column separation lines.
  111. * Default is <tt>false</tt>.
  112. */
  113. columnLines : false,
  114. /**
  115. * @cfg {Object} cm Shorthand for <tt>{@link #colModel}</tt>.
  116. */
  117. /**
  118. * @cfg {Object} colModel The {@link Ext.grid.ColumnModel} to use when rendering the grid (required).
  119. */
  120. /**
  121. * @cfg {Array} columns An array of {@link Ext.grid.Column columns} to auto create a
  122. * {@link Ext.grid.ColumnModel}. The ColumnModel may be explicitly created via the
  123. * <tt>{@link #colModel}</tt> configuration property.
  124. */
  125. /**
  126. * @cfg {String} ddGroup The DD group this GridPanel belongs to. Defaults to <tt>'GridDD'</tt> if not specified.
  127. */
  128. /**
  129. * @cfg {String} ddText
  130. * Configures the text in the drag proxy. Defaults to:
  131. * <pre><code>
  132. * ddText : '{0} selected row{1}'
  133. * </code></pre>
  134. * <tt>{0}</tt> is replaced with the number of selected rows.
  135. */
  136. ddText : '{0} selected row{1}',
  137. /**
  138. * @cfg {Boolean} deferRowRender <P>Defaults to <tt>true</tt> to enable deferred row rendering.</p>
  139. * <p>This allows the GridPanel to be initially rendered empty, with the expensive update of the row
  140. * structure deferred so that layouts with GridPanels appear more quickly.</p>
  141. */
  142. deferRowRender : true,
  143. /**
  144. * @cfg {Boolean} disableSelection <p><tt>true</tt> to disable selections in the grid. Defaults to <tt>false</tt>.</p>
  145. * <p>Ignored if a {@link #selModel SelectionModel} is specified.</p>
  146. */
  147. /**
  148. * @cfg {Boolean} enableColumnResize <tt>false</tt> to turn off column resizing for the whole grid. Defaults to <tt>true</tt>.
  149. */
  150. /**
  151. * @cfg {Boolean} enableColumnHide
  152. * Defaults to <tt>true</tt> to enable {@link Ext.grid.Column#hidden hiding of columns}
  153. * with the {@link #enableHdMenu header menu}.
  154. */
  155. enableColumnHide : true,
  156. /**
  157. * @cfg {Boolean} enableColumnMove Defaults to <tt>true</tt> to enable drag and drop reorder of columns. <tt>false</tt>
  158. * to turn off column reordering via drag drop.
  159. */
  160. enableColumnMove : true,
  161. /**
  162. * @cfg {Boolean} enableDragDrop <p>Enables dragging of the selected rows of the GridPanel. Defaults to <tt>false</tt>.</p>
  163. * <p>Setting this to <b><tt>true</tt></b> causes this GridPanel's {@link #getView GridView} to
  164. * create an instance of {@link Ext.grid.GridDragZone}. <b>Note</b>: this is available only <b>after</b>
  165. * the Grid has been rendered as the GridView's <tt>{@link Ext.grid.GridView#dragZone dragZone}</tt>
  166. * property.</p>
  167. * <p>A cooperating {@link Ext.dd.DropZone DropZone} must be created who's implementations of
  168. * {@link Ext.dd.DropZone#onNodeEnter onNodeEnter}, {@link Ext.dd.DropZone#onNodeOver onNodeOver},
  169. * {@link Ext.dd.DropZone#onNodeOut onNodeOut} and {@link Ext.dd.DropZone#onNodeDrop onNodeDrop} are able
  170. * to process the {@link Ext.grid.GridDragZone#getDragData data} which is provided.</p>
  171. */
  172. enableDragDrop : false,
  173. /**
  174. * @cfg {Boolean} enableHdMenu Defaults to <tt>true</tt> to enable the drop down button for menu in the headers.
  175. */
  176. enableHdMenu : true,
  177. /**
  178. * @cfg {Boolean} hideHeaders True to hide the grid's header. Defaults to <code>false</code>.
  179. */
  180. /**
  181. * @cfg {Object} loadMask An {@link Ext.LoadMask} config or true to mask the grid while
  182. * loading. Defaults to <code>false</code>.
  183. */
  184. loadMask : false,
  185. /**
  186. * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if <tt>autoHeight</tt> is not on.
  187. */
  188. /**
  189. * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Defaults to <tt>25</tt>.
  190. */
  191. minColumnWidth : 25,
  192. /**
  193. * @cfg {Object} sm Shorthand for <tt>{@link #selModel}</tt>.
  194. */
  195. /**
  196. * @cfg {Object} selModel Any subclass of {@link Ext.grid.AbstractSelectionModel} that will provide
  197. * the selection model for the grid (defaults to {@link Ext.grid.RowSelectionModel} if not specified).
  198. */
  199. /**
  200. * @cfg {Ext.data.Store} store The {@link Ext.data.Store} the grid should use as its data source (required).
  201. */
  202. /**
  203. * @cfg {Boolean} stripeRows <tt>true</tt> to stripe the rows. Default is <tt>false</tt>.
  204. * <p>This causes the CSS class <tt><b>x-grid3-row-alt</b></tt> to be added to alternate rows of
  205. * the grid. A default CSS rule is provided which sets a background colour, but you can override this
  206. * with a rule which either overrides the <b>background-color</b> style using the '!important'
  207. * modifier, or which uses a CSS selector of higher specificity.</p>
  208. */
  209. stripeRows : false,
  210. /**
  211. * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is <tt>true</tt>
  212. * for GridPanel, but <tt>false</tt> for EditorGridPanel.
  213. */
  214. trackMouseOver : true,
  215. /**
  216. * @cfg {Array} stateEvents
  217. * An array of events that, when fired, should trigger this component to save its state.
  218. * Defaults to:<pre><code>
  219. * stateEvents: ['columnmove', 'columnresize', 'sortchange', 'groupchange']
  220. * </code></pre>
  221. * <p>These can be any types of events supported by this component, including browser or
  222. * custom events (e.g., <tt>['click', 'customerchange']</tt>).</p>
  223. * <p>See {@link Ext.Component#stateful} for an explanation of saving and restoring
  224. * Component state.</p>
  225. */
  226. stateEvents : ['columnmove', 'columnresize', 'sortchange', 'groupchange'],
  227. /**
  228. * @cfg {Object} view The {@link Ext.grid.GridView} used by the grid. This can be set
  229. * before a call to {@link Ext.Component#render render()}.
  230. */
  231. view : null,
  232. /**
  233. * @cfg {Array} bubbleEvents
  234. * <p>An array of events that, when fired, should be bubbled to any parent container.
  235. * See {@link Ext.util.Observable#enableBubble}.
  236. * Defaults to <tt>[]</tt>.
  237. */
  238. bubbleEvents: [],
  239. /**
  240. * @cfg {Object} viewConfig A config object that will be applied to the grid's UI view. Any of
  241. * the config options available for {@link Ext.grid.GridView} can be specified here. This option
  242. * is ignored if <tt>{@link #view}</tt> is specified.
  243. */
  244. // private
  245. rendered : false,
  246. // private
  247. viewReady : false,
  248. // private
  249. initComponent : function(){
  250. Ext.grid.GridPanel.superclass.initComponent.call(this);
  251. if(this.columnLines){
  252. this.cls = (this.cls || '') + ' x-grid-with-col-lines';
  253. }
  254. // override any provided value since it isn't valid
  255. // and is causing too many bug reports ;)
  256. this.autoScroll = false;
  257. this.autoWidth = false;
  258. if(Ext.isArray(this.columns)){
  259. this.colModel = new Ext.grid.ColumnModel(this.columns);
  260. delete this.columns;
  261. }
  262. // check and correct shorthanded configs
  263. if(this.ds){
  264. this.store = this.ds;
  265. delete this.ds;
  266. }
  267. if(this.cm){
  268. this.colModel = this.cm;
  269. delete this.cm;
  270. }
  271. if(this.sm){
  272. this.selModel = this.sm;
  273. delete this.sm;
  274. }
  275. this.store = Ext.StoreMgr.lookup(this.store);
  276. this.addEvents(
  277. // raw events
  278. /**
  279. * @event click
  280. * The raw click event for the entire grid.
  281. * @param {Ext.EventObject} e
  282. */
  283. 'click',
  284. /**
  285. * @event dblclick
  286. * The raw dblclick event for the entire grid.
  287. * @param {Ext.EventObject} e
  288. */
  289. 'dblclick',
  290. /**
  291. * @event contextmenu
  292. * The raw contextmenu event for the entire grid.
  293. * @param {Ext.EventObject} e
  294. */
  295. 'contextmenu',
  296. /**
  297. * @event mousedown
  298. * The raw mousedown event for the entire grid.
  299. * @param {Ext.EventObject} e
  300. */
  301. 'mousedown',
  302. /**
  303. * @event mouseup
  304. * The raw mouseup event for the entire grid.
  305. * @param {Ext.EventObject} e
  306. */
  307. 'mouseup',
  308. /**
  309. * @event mouseover
  310. * The raw mouseover event for the entire grid.
  311. * @param {Ext.EventObject} e
  312. */
  313. 'mouseover',
  314. /**
  315. * @event mouseout
  316. * The raw mouseout event for the entire grid.
  317. * @param {Ext.EventObject} e
  318. */
  319. 'mouseout',
  320. /**
  321. * @event keypress
  322. * The raw keypress event for the entire grid.
  323. * @param {Ext.EventObject} e
  324. */
  325. 'keypress',
  326. /**
  327. * @event keydown
  328. * The raw keydown event for the entire grid.
  329. * @param {Ext.EventObject} e
  330. */
  331. 'keydown',
  332. // custom events
  333. /**
  334. * @event cellmousedown
  335. * Fires before a cell is clicked
  336. * @param {Grid} this
  337. * @param {Number} rowIndex
  338. * @param {Number} columnIndex
  339. * @param {Ext.EventObject} e
  340. */
  341. 'cellmousedown',
  342. /**
  343. * @event rowmousedown
  344. * Fires before a row is clicked
  345. * @param {Grid} this
  346. * @param {Number} rowIndex
  347. * @param {Ext.EventObject} e
  348. */
  349. 'rowmousedown',
  350. /**
  351. * @event headermousedown
  352. * Fires before a header is clicked
  353. * @param {Grid} this
  354. * @param {Number} columnIndex
  355. * @param {Ext.EventObject} e
  356. */
  357. 'headermousedown',
  358. /**
  359. * @event groupmousedown
  360. * Fires before a group header is clicked. <b>Only applies for grids with a {@link Ext.grid.GroupingView GroupingView}</b>.
  361. * @param {Grid} this
  362. * @param {String} groupField
  363. * @param {String} groupValue
  364. * @param {Ext.EventObject} e
  365. */
  366. 'groupmousedown',
  367. /**
  368. * @event rowbodymousedown
  369. * Fires before the row body is clicked. <b>Only applies for grids with {@link Ext.grid.GridView#enableRowBody enableRowBody} configured.</b>
  370. * @param {Grid} this
  371. * @param {Number} rowIndex
  372. * @param {Ext.EventObject} e
  373. */
  374. 'rowbodymousedown',
  375. /**
  376. * @event containermousedown
  377. * Fires before the container is clicked. The container consists of any part of the grid body that is not covered by a row.
  378. * @param {Grid} this
  379. * @param {Ext.EventObject} e
  380. */
  381. 'containermousedown',
  382. /**
  383. * @event cellclick
  384. * Fires when a cell is clicked.
  385. * The data for the cell is drawn from the {@link Ext.data.Record Record}
  386. * for this row. To access the data in the listener function use the
  387. * following technique:
  388. * <pre><code>
  389. function(grid, rowIndex, columnIndex, e) {
  390. var record = grid.getStore().getAt(rowIndex); // Get the Record
  391. var fieldName = grid.getColumnModel().getDataIndex(columnIndex); // Get field name
  392. var data = record.get(fieldName);
  393. }
  394. </code></pre>
  395. * @param {Grid} this
  396. * @param {Number} rowIndex
  397. * @param {Number} columnIndex
  398. * @param {Ext.EventObject} e
  399. */
  400. 'cellclick',
  401. /**
  402. * @event celldblclick
  403. * Fires when a cell is double clicked
  404. * @param {Grid} this
  405. * @param {Number} rowIndex
  406. * @param {Number} columnIndex
  407. * @param {Ext.EventObject} e
  408. */
  409. 'celldblclick',
  410. /**
  411. * @event rowclick
  412. * Fires when a row is clicked
  413. * @param {Grid} this
  414. * @param {Number} rowIndex
  415. * @param {Ext.EventObject} e
  416. */
  417. 'rowclick',
  418. /**
  419. * @event rowdblclick
  420. * Fires when a row is double clicked
  421. * @param {Grid} this
  422. * @param {Number} rowIndex
  423. * @param {Ext.EventObject} e
  424. */
  425. 'rowdblclick',
  426. /**
  427. * @event headerclick
  428. * Fires when a header is clicked
  429. * @param {Grid} this
  430. * @param {Number} columnIndex
  431. * @param {Ext.EventObject} e
  432. */
  433. 'headerclick',
  434. /**
  435. * @event headerdblclick
  436. * Fires when a header cell is double clicked
  437. * @param {Grid} this
  438. * @param {Number} columnIndex
  439. * @param {Ext.EventObject} e
  440. */
  441. 'headerdblclick',
  442. /**
  443. * @event groupclick
  444. * Fires when group header is clicked. <b>Only applies for grids with a {@link Ext.grid.GroupingView GroupingView}</b>.
  445. * @param {Grid} this
  446. * @param {String} groupField
  447. * @param {String} groupValue
  448. * @param {Ext.EventObject} e
  449. */
  450. 'groupclick',
  451. /**
  452. * @event groupdblclick
  453. * Fires when group header is double clicked. <b>Only applies for grids with a {@link Ext.grid.GroupingView GroupingView}</b>.
  454. * @param {Grid} this
  455. * @param {String} groupField
  456. * @param {String} groupValue
  457. * @param {Ext.EventObject} e
  458. */
  459. 'groupdblclick',
  460. /**
  461. * @event containerclick
  462. * Fires when the container is clicked. The container consists of any part of the grid body that is not covered by a row.
  463. * @param {Grid} this
  464. * @param {Ext.EventObject} e
  465. */
  466. 'containerclick',
  467. /**
  468. * @event containerdblclick
  469. * Fires when the container is double clicked. The container consists of any part of the grid body that is not covered by a row.
  470. * @param {Grid} this
  471. * @param {Ext.EventObject} e
  472. */
  473. 'containerdblclick',
  474. /**
  475. * @event rowbodyclick
  476. * Fires when the row body is clicked. <b>Only applies for grids with {@link Ext.grid.GridView#enableRowBody enableRowBody} configured.</b>
  477. * @param {Grid} this
  478. * @param {Number} rowIndex
  479. * @param {Ext.EventObject} e
  480. */
  481. 'rowbodyclick',
  482. /**
  483. * @event rowbodydblclick
  484. * Fires when the row body is double clicked. <b>Only applies for grids with {@link Ext.grid.GridView#enableRowBody enableRowBody} configured.</b>
  485. * @param {Grid} this
  486. * @param {Number} rowIndex
  487. * @param {Ext.EventObject} e
  488. */
  489. 'rowbodydblclick',
  490. /**
  491. * @event rowcontextmenu
  492. * Fires when a row is right clicked
  493. * @param {Grid} this
  494. * @param {Number} rowIndex
  495. * @param {Ext.EventObject} e
  496. */
  497. 'rowcontextmenu',
  498. /**
  499. * @event cellcontextmenu
  500. * Fires when a cell is right clicked
  501. * @param {Grid} this
  502. * @param {Number} rowIndex
  503. * @param {Number} cellIndex
  504. * @param {Ext.EventObject} e
  505. */
  506. 'cellcontextmenu',
  507. /**
  508. * @event headercontextmenu
  509. * Fires when a header is right clicked
  510. * @param {Grid} this
  511. * @param {Number} columnIndex
  512. * @param {Ext.EventObject} e
  513. */
  514. 'headercontextmenu',
  515. /**
  516. * @event groupcontextmenu
  517. * Fires when group header is right clicked. <b>Only applies for grids with a {@link Ext.grid.GroupingView GroupingView}</b>.
  518. * @param {Grid} this
  519. * @param {String} groupField
  520. * @param {String} groupValue
  521. * @param {Ext.EventObject} e
  522. */
  523. 'groupcontextmenu',
  524. /**
  525. * @event containercontextmenu
  526. * Fires when the container is right clicked. The container consists of any part of the grid body that is not covered by a row.
  527. * @param {Grid} this
  528. * @param {Ext.EventObject} e
  529. */
  530. 'containercontextmenu',
  531. /**
  532. * @event rowbodycontextmenu
  533. * Fires when the row body is right clicked. <b>Only applies for grids with {@link Ext.grid.GridView#enableRowBody enableRowBody} configured.</b>
  534. * @param {Grid} this
  535. * @param {Number} rowIndex
  536. * @param {Ext.EventObject} e
  537. */
  538. 'rowbodycontextmenu',
  539. /**
  540. * @event bodyscroll
  541. * Fires when the body element is scrolled
  542. * @param {Number} scrollLeft
  543. * @param {Number} scrollTop
  544. */
  545. 'bodyscroll',
  546. /**
  547. * @event columnresize
  548. * Fires when the user resizes a column
  549. * @param {Number} columnIndex
  550. * @param {Number} newSize
  551. */
  552. 'columnresize',
  553. /**
  554. * @event columnmove
  555. * Fires when the user moves a column
  556. * @param {Number} oldIndex
  557. * @param {Number} newIndex
  558. */
  559. 'columnmove',
  560. /**
  561. * @event sortchange
  562. * Fires when the grid's store sort changes
  563. * @param {Grid} this
  564. * @param {Object} sortInfo An object with the keys field and direction
  565. */
  566. 'sortchange',
  567. /**
  568. * @event groupchange
  569. * Fires when the grid's grouping changes (only applies for grids with a {@link Ext.grid.GroupingView GroupingView})
  570. * @param {Grid} this
  571. * @param {String} groupField A string with the grouping field, null if the store is not grouped.
  572. */
  573. 'groupchange',
  574. /**
  575. * @event reconfigure
  576. * Fires when the grid is reconfigured with a new store and/or column model.
  577. * @param {Grid} this
  578. * @param {Ext.data.Store} store The new store
  579. * @param {Ext.grid.ColumnModel} colModel The new column model
  580. */
  581. 'reconfigure',
  582. /**
  583. * @event viewready
  584. * Fires when the grid view is available (use this for selecting a default row).
  585. * @param {Grid} this
  586. */
  587. 'viewready'
  588. );
  589. },
  590. // private
  591. onRender : function(ct, position){
  592. Ext.grid.GridPanel.superclass.onRender.apply(this, arguments);
  593. var c = this.getGridEl();
  594. this.el.addClass('x-grid-panel');
  595. this.mon(c, {
  596. scope: this,
  597. mousedown: this.onMouseDown,
  598. click: this.onClick,
  599. dblclick: this.onDblClick,
  600. contextmenu: this.onContextMenu
  601. });
  602. this.relayEvents(c, ['mousedown','mouseup','mouseover','mouseout','keypress', 'keydown']);
  603. var view = this.getView();
  604. view.init(this);
  605. view.render();
  606. this.getSelectionModel().init(this);
  607. },
  608. // private
  609. initEvents : function(){
  610. Ext.grid.GridPanel.superclass.initEvents.call(this);
  611. if(this.loadMask){
  612. this.loadMask = new Ext.LoadMask(this.bwrap,
  613. Ext.apply({store:this.store}, this.loadMask));
  614. }
  615. },
  616. initStateEvents : function(){
  617. Ext.grid.GridPanel.superclass.initStateEvents.call(this);
  618. this.mon(this.colModel, 'hiddenchange', this.saveState, this, {delay: 100});
  619. },
  620. applyState : function(state){
  621. var cm = this.colModel,
  622. cs = state.columns,
  623. store = this.store,
  624. s,
  625. c,
  626. oldIndex;
  627. if(cs){
  628. for(var i = 0, len = cs.length; i < len; i++){
  629. s = cs[i];
  630. c = cm.getColumnById(s.id);
  631. if(c){
  632. c.hidden = s.hidden;
  633. c.width = s.width;
  634. oldIndex = cm.getIndexById(s.id);
  635. if(oldIndex != i){
  636. cm.moveColumn(oldIndex, i);
  637. }
  638. }
  639. }
  640. }
  641. if(store){
  642. s = state.sort;
  643. if(s){
  644. store[store.remoteSort ? 'setDefaultSort' : 'sort'](s.field, s.direction);
  645. }
  646. s = state.group;
  647. if(store.groupBy){
  648. if(s){
  649. store.groupBy(s);
  650. }else{
  651. store.clearGrouping();
  652. }
  653. }
  654. }
  655. var o = Ext.apply({}, state);
  656. delete o.columns;
  657. delete o.sort;
  658. Ext.grid.GridPanel.superclass.applyState.call(this, o);
  659. },
  660. getState : function(){
  661. var o = {columns: []},
  662. store = this.store,
  663. ss,
  664. gs;
  665. for(var i = 0, c; (c = this.colModel.config[i]); i++){
  666. o.columns[i] = {
  667. id: c.id,
  668. width: c.width
  669. };
  670. if(c.hidden){
  671. o.columns[i].hidden = true;
  672. }
  673. }
  674. if(store){
  675. ss = store.getSortState();
  676. if(ss){
  677. o.sort = ss;
  678. }
  679. if(store.getGroupState){
  680. gs = store.getGroupState();
  681. if(gs){
  682. o.group = gs;
  683. }
  684. }
  685. }
  686. return o;
  687. },
  688. // private
  689. afterRender : function(){
  690. Ext.grid.GridPanel.superclass.afterRender.call(this);
  691. var v = this.view;
  692. this.on('bodyresize', v.layout, v);
  693. v.layout();
  694. if(this.deferRowRender){
  695. if (!this.deferRowRenderTask){
  696. this.deferRowRenderTask = new Ext.util.DelayedTask(v.afterRender, this.view);
  697. }
  698. this.deferRowRenderTask.delay(10);
  699. }else{
  700. v.afterRender();
  701. }
  702. this.viewReady = true;
  703. },
  704. /**
  705. * <p>Reconfigures the grid to use a different Store and Column Model
  706. * and fires the 'reconfigure' event. The View will be bound to the new
  707. * objects and refreshed.</p>
  708. * <p>Be aware that upon reconfiguring a GridPanel, certain existing settings <i>may</i> become
  709. * invalidated. For example the configured {@link #autoExpandColumn} may no longer exist in the
  710. * new ColumnModel. Also, an existing {@link Ext.PagingToolbar PagingToolbar} will still be bound
  711. * to the old Store, and will need rebinding. Any {@link #plugins} might also need reconfiguring
  712. * with the new data.</p>
  713. * @param {Ext.data.Store} store The new {@link Ext.data.Store} object
  714. * @param {Ext.grid.ColumnModel} colModel The new {@link Ext.grid.ColumnModel} object
  715. */
  716. reconfigure : function(store, colModel){
  717. var rendered = this.rendered;
  718. if(rendered){
  719. if(this.loadMask){
  720. this.loadMask.destroy();
  721. this.loadMask = new Ext.LoadMask(this.bwrap,
  722. Ext.apply({}, {store:store}, this.initialConfig.loadMask));
  723. }
  724. }
  725. if(this.view){
  726. this.view.initData(store, colModel);
  727. }
  728. this.store = store;
  729. this.colModel = colModel;
  730. if(rendered){
  731. this.view.refresh(true);
  732. }
  733. this.fireEvent('reconfigure', this, store, colModel);
  734. },
  735. // private
  736. onDestroy : function(){
  737. if (this.deferRowRenderTask && this.deferRowRenderTask.cancel){
  738. this.deferRowRenderTask.cancel();
  739. }
  740. if(this.rendered){
  741. Ext.destroy(this.view, this.loadMask);
  742. }else if(this.store && this.store.autoDestroy){
  743. this.store.destroy();
  744. }
  745. Ext.destroy(this.colModel, this.selModel);
  746. this.store = this.selModel = this.colModel = this.view = this.loadMask = null;
  747. Ext.grid.GridPanel.superclass.onDestroy.call(this);
  748. },
  749. // private
  750. processEvent : function(name, e){
  751. this.view.processEvent(name, e);
  752. },
  753. // private
  754. onClick : function(e){
  755. this.processEvent('click', e);
  756. },
  757. // private
  758. onMouseDown : function(e){
  759. this.processEvent('mousedown', e);
  760. },
  761. // private
  762. onContextMenu : function(e, t){
  763. this.processEvent('contextmenu', e);
  764. },
  765. // private
  766. onDblClick : function(e){
  767. this.processEvent('dblclick', e);
  768. },
  769. // private
  770. walkCells : function(row, col, step, fn, scope){
  771. var cm = this.colModel,
  772. clen = cm.getColumnCount(),
  773. ds = this.store,
  774. rlen = ds.getCount(),
  775. first = true;
  776. if(step < 0){
  777. if(col < 0){
  778. row--;
  779. first = false;
  780. }
  781. while(row >= 0){
  782. if(!first){
  783. col = clen-1;
  784. }
  785. first = false;
  786. while(col >= 0){
  787. if(fn.call(scope || this, row, col, cm) === true){
  788. return [row, col];
  789. }
  790. col--;
  791. }
  792. row--;
  793. }
  794. } else {
  795. if(col >= clen){
  796. row++;
  797. first = false;
  798. }
  799. while(row < rlen){
  800. if(!first){
  801. col = 0;
  802. }
  803. first = false;
  804. while(col < clen){
  805. if(fn.call(scope || this, row, col, cm) === true){
  806. return [row, col];
  807. }
  808. col++;
  809. }
  810. row++;
  811. }
  812. }
  813. return null;
  814. },
  815. /**
  816. * Returns the grid's underlying element.
  817. * @return {Element} The element
  818. */
  819. getGridEl : function(){
  820. return this.body;
  821. },
  822. // private for compatibility, overridden by editor grid
  823. stopEditing : Ext.emptyFn,
  824. /**
  825. * Returns the grid's selection model configured by the <code>{@link #selModel}</code>
  826. * configuration option. If no selection model was configured, this will create
  827. * and return a {@link Ext.grid.RowSelectionModel RowSelectionModel}.
  828. * @return {SelectionModel}
  829. */
  830. getSelectionModel : function(){
  831. if(!this.selModel){
  832. this.selModel = new Ext.grid.RowSelectionModel(
  833. this.disableSelection ? {selectRow: Ext.emptyFn} : null);
  834. }
  835. return this.selModel;
  836. },
  837. /**
  838. * Returns the grid's data store.
  839. * @return {Ext.data.Store} The store
  840. */
  841. getStore : function(){
  842. return this.store;
  843. },
  844. /**
  845. * Returns the grid's ColumnModel.
  846. * @return {Ext.grid.ColumnModel} The column model
  847. */
  848. getColumnModel : function(){
  849. return this.colModel;
  850. },
  851. /**
  852. * Returns the grid's GridView object.
  853. * @return {Ext.grid.GridView} The grid view
  854. */
  855. getView : function(){
  856. if(!this.view){
  857. this.view = new Ext.grid.GridView(this.viewConfig);
  858. }
  859. return this.view;
  860. },
  861. /**
  862. * Called to get grid's drag proxy text, by default returns this.ddText.
  863. * @return {String} The text
  864. */
  865. getDragDropText : function(){
  866. var count = this.selModel.getCount();
  867. return String.format(this.ddText, count, count == 1 ? '' : 's');
  868. }
  869. /**
  870. * @cfg {String/Number} activeItem
  871. * @hide
  872. */
  873. /**
  874. * @cfg {Boolean} autoDestroy
  875. * @hide
  876. */
  877. /**
  878. * @cfg {Object/String/Function} autoLoad
  879. * @hide
  880. */
  881. /**
  882. * @cfg {Boolean} autoWidth
  883. * @hide
  884. */
  885. /**
  886. * @cfg {Boolean/Number} bufferResize
  887. * @hide
  888. */
  889. /**
  890. * @cfg {String} defaultType
  891. * @hide
  892. */
  893. /**
  894. * @cfg {Object} defaults
  895. * @hide
  896. */
  897. /**
  898. * @cfg {Boolean} hideBorders
  899. * @hide
  900. */
  901. /**
  902. * @cfg {Mixed} items
  903. * @hide
  904. */
  905. /**
  906. * @cfg {String} layout
  907. * @hide
  908. */
  909. /**
  910. * @cfg {Object} layoutConfig
  911. * @hide
  912. */
  913. /**
  914. * @cfg {Boolean} monitorResize
  915. * @hide
  916. */
  917. /**
  918. * @property items
  919. * @hide
  920. */
  921. /**
  922. * @method add
  923. * @hide
  924. */
  925. /**
  926. * @method cascade
  927. * @hide
  928. */
  929. /**
  930. * @method doLayout
  931. * @hide
  932. */
  933. /**
  934. * @method find
  935. * @hide
  936. */
  937. /**
  938. * @method findBy
  939. * @hide
  940. */
  941. /**
  942. * @method findById
  943. * @hide
  944. */
  945. /**
  946. * @method findByType
  947. * @hide
  948. */
  949. /**
  950. * @method getComponent
  951. * @hide
  952. */
  953. /**
  954. * @method getLayout
  955. * @hide
  956. */
  957. /**
  958. * @method getUpdater
  959. * @hide
  960. */
  961. /**
  962. * @method insert
  963. * @hide
  964. */
  965. /**
  966. * @method load
  967. * @hide
  968. */
  969. /**
  970. * @method remove
  971. * @hide
  972. */
  973. /**
  974. * @event add
  975. * @hide
  976. */
  977. /**
  978. * @event afterlayout
  979. * @hide
  980. */
  981. /**
  982. * @event beforeadd
  983. * @hide
  984. */
  985. /**
  986. * @event beforeremove
  987. * @hide
  988. */
  989. /**
  990. * @event remove
  991. * @hide
  992. */
  993. /**
  994. * @cfg {String} allowDomMove @hide
  995. */
  996. /**
  997. * @cfg {String} autoEl @hide
  998. */
  999. /**
  1000. * @cfg {String} applyTo @hide
  1001. */
  1002. /**
  1003. * @cfg {String} autoScroll @hide
  1004. */
  1005. /**
  1006. * @cfg {String} bodyBorder @hide
  1007. */
  1008. /**
  1009. * @cfg {String} bodyStyle @hide
  1010. */
  1011. /**
  1012. * @cfg {String} contentEl @hide
  1013. */
  1014. /**
  1015. * @cfg {String} disabledClass @hide
  1016. */
  1017. /**
  1018. * @cfg {String} elements @hide
  1019. */
  1020. /**
  1021. * @cfg {String} html @hide
  1022. */
  1023. /**
  1024. * @cfg {Boolean} preventBodyReset
  1025. * @hide
  1026. */
  1027. /**
  1028. * @property disabled
  1029. * @hide
  1030. */
  1031. /**
  1032. * @method applyToMarkup
  1033. * @hide
  1034. */
  1035. /**
  1036. * @method enable
  1037. * @hide
  1038. */
  1039. /**
  1040. * @method disable
  1041. * @hide
  1042. */
  1043. /**
  1044. * @method setDisabled
  1045. * @hide
  1046. */
  1047. });
  1048. Ext.reg('grid', Ext.grid.GridPanel);/**
  1049. * @class Ext.grid.GridView
  1050. * @extends Ext.util.Observable
  1051. * <p>This class encapsulates the user interface of an {@link Ext.grid.GridPanel}.
  1052. * Methods of this class may be used to access user interface elements to enable
  1053. * special display effects. Do not change the DOM structure of the user interface.</p>
  1054. * <p>This class does not provide ways to manipulate the underlying data. The data
  1055. * model of a Grid is held in an {@link Ext.data.Store}.</p>
  1056. * @constructor
  1057. * @param {Object} config
  1058. */
  1059. Ext.grid.GridView = Ext.extend(Ext.util.Observable, {
  1060. /**
  1061. * Override this function to apply custom CSS classes to rows during rendering. You can also supply custom
  1062. * parameters to the row template for the current row to customize how it is rendered using the <b>rowParams</b>
  1063. * parameter. This function should return the CSS class name (or empty string '' for none) that will be added
  1064. * to the row's wrapping div. To apply multiple class names, simply return them space-delimited within the string
  1065. * (e.g., 'my-class another-class'). Example usage:
  1066. <pre><code>
  1067. viewConfig: {
  1068. forceFit: true,
  1069. showPreview: true, // custom property
  1070. enableRowBody: true, // required to create a second, full-width row to show expanded Record data
  1071. getRowClass: function(record, rowIndex, rp, ds){ // rp = rowParams
  1072. if(this.showPreview){
  1073. rp.body = '&lt;p>'+record.data.excerpt+'&lt;/p>';
  1074. return 'x-grid3-row-expanded';
  1075. }
  1076. return 'x-grid3-row-collapsed';
  1077. }
  1078. },
  1079. </code></pre>
  1080. * @param {Record} record The {@link Ext.data.Record} corresponding to the current row.
  1081. * @param {Number} index The row index.
  1082. * @param {Object} rowParams A config object that is passed to the row template during rendering that allows
  1083. * customization of various aspects of a grid row.
  1084. * <p>If {@link #enableRowBody} is configured <b><tt></tt>true</b>, then the following properties may be set
  1085. * by this function, and will be used to render a full-width expansion row below each grid row:</p>
  1086. * <ul>
  1087. * <li><code>body</code> : String <div class="sub-desc">An HTML fragment to be used as the expansion row's body content (defaults to '').</div></li>
  1088. * <li><code>bodyStyle</code> : String <div class="sub-desc">A CSS style specification that will be applied to the expansion row's &lt;tr> element. (defaults to '').</div></li>
  1089. * </ul>
  1090. * The following property will be passed in, and may be appended to:
  1091. * <ul>
  1092. * <li><code>tstyle</code> : String <div class="sub-desc">A CSS style specification that willl be applied to the &lt;table> element which encapsulates
  1093. * both the standard grid row, and any expansion row.</div></li>
  1094. * </ul>
  1095. * @param {Store} store The {@link Ext.data.Store} this grid is bound to
  1096. * @method getRowClass
  1097. * @return {String} a CSS class name to add to the row.
  1098. */
  1099. /**
  1100. * @cfg {Boolean} enableRowBody True to add a second TR element per row that can be used to provide a row body
  1101. * that spans beneath the data row. Use the {@link #getRowClass} method's rowParams config to customize the row body.
  1102. */
  1103. /**
  1104. * @cfg {String} emptyText Default text (html tags are accepted) to display in the grid body when no rows
  1105. * are available (defaults to ''). This value will be used to update the <tt>{@link #mainBody}</tt>:
  1106. <pre><code>
  1107. this.mainBody.update('&lt;div class="x-grid-empty">' + this.emptyText + '&lt;/div>');
  1108. </code></pre>
  1109. */
  1110. /**
  1111. * @cfg {Boolean} headersDisabled True to disable the grid column headers (defaults to <tt>false</tt>).
  1112. * Use the {@link Ext.grid.ColumnModel ColumnModel} <tt>{@link Ext.grid.ColumnModel#menuDisabled menuDisabled}</tt>
  1113. * config to disable the <i>menu</i> for individual columns. While this config is true the
  1114. * following will be disabled:<div class="mdetail-params"><ul>
  1115. * <li>clicking on header to sort</li>
  1116. * <li>the trigger to reveal the menu.</li>
  1117. * </ul></div>
  1118. */
  1119. /**
  1120. * <p>A customized implementation of a {@link Ext.dd.DragZone DragZone} which provides default implementations
  1121. * of the template methods of DragZone to enable dragging of the selected rows of a GridPanel.
  1122. * See {@link Ext.grid.GridDragZone} for details.</p>
  1123. * <p>This will <b>only</b> be present:<div class="mdetail-params"><ul>
  1124. * <li><i>if</i> the owning GridPanel was configured with {@link Ext.grid.GridPanel#enableDragDrop enableDragDrop}: <tt>true</tt>.</li>
  1125. * <li><i>after</i> the owning GridPanel has been rendered.</li>
  1126. * </ul></div>
  1127. * @property dragZone
  1128. * @type {Ext.grid.GridDragZone}
  1129. */
  1130. /**
  1131. * @cfg {Boolean} deferEmptyText True to defer <tt>{@link #emptyText}</tt> being applied until the store's
  1132. * first load (defaults to <tt>true</tt>).
  1133. */
  1134. deferEmptyText : true,
  1135. /**
  1136. * @cfg {Number} scrollOffset The amount of space to reserve for the vertical scrollbar
  1137. * (defaults to <tt>undefined</tt>). If an explicit value isn't specified, this will be automatically
  1138. * calculated.
  1139. */
  1140. scrollOffset : undefined,
  1141. /**
  1142. * @cfg {Boolean} autoFill
  1143. * Defaults to <tt>false</tt>. Specify <tt>true</tt> to have the column widths re-proportioned
  1144. * when the grid is <b>initially rendered</b>. The
  1145. * {@link Ext.grid.Column#width initially configured width}</tt> of each column will be adjusted
  1146. * to fit the grid width and prevent horizontal scrolling. If columns are later resized (manually
  1147. * or programmatically), the other columns in the grid will <b>not</b> be resized to fit the grid width.
  1148. * See <tt>{@link #forceFit}</tt> also.
  1149. */
  1150. autoFill : false,
  1151. /**
  1152. * @cfg {Boolean} forceFit
  1153. * Defaults to <tt>false</tt>. Specify <tt>true</tt> to have the column widths re-proportioned
  1154. * at <b>all times</b>. The {@link Ext.grid.Column#width initially configured width}</tt> of each
  1155. * column will be adjusted to fit the grid width and prevent horizontal scrolling. If columns are
  1156. * later resized (manually or programmatically), the other columns in the grid <b>will</b> be resized
  1157. * to fit the grid width. See <tt>{@link #autoFill}</tt> also.
  1158. */
  1159. forceFit : false,
  1160. /**
  1161. * @cfg {Array} sortClasses The CSS classes applied to a header when it is sorted. (defaults to <tt>['sort-asc', 'sort-desc']</tt>)
  1162. */
  1163. sortClasses : ['sort-asc', 'sort-desc'],
  1164. /**
  1165. * @cfg {String} sortAscText The text displayed in the 'Sort Ascending' menu item (defaults to <tt>'Sort Ascending'</tt>)
  1166. */
  1167. sortAscText : 'Sort Ascending',
  1168. /**
  1169. * @cfg {String} sortDescText The text displayed in the 'Sort Descending' menu item (defaults to <tt>'Sort Descending'</tt>)
  1170. */
  1171. sortDescText : 'Sort Descending',
  1172. /**
  1173. * @cfg {String} columnsText The text displayed in the 'Columns' menu item (defaults to <tt>'Columns'</tt>)
  1174. */
  1175. columnsText : 'Columns',
  1176. /**
  1177. * @cfg {String} selectedRowClass The CSS class applied to a selected row (defaults to <tt>'x-grid3-row-selected'</tt>). An
  1178. * example overriding the default styling:
  1179. <pre><code>
  1180. .x-grid3-row-selected {background-color: yellow;}
  1181. </code></pre>
  1182. * Note that this only controls the row, and will not do anything for the text inside it. To style inner
  1183. * facets (like text) use something like:
  1184. <pre><code>
  1185. .x-grid3-row-selected .x-grid3-cell-inner {
  1186. color: #FFCC00;
  1187. }
  1188. </code></pre>
  1189. * @type String
  1190. */
  1191. selectedRowClass : 'x-grid3-row-selected',
  1192. // private
  1193. borderWidth : 2,
  1194. tdClass : 'x-grid3-cell',
  1195. hdCls : 'x-grid3-hd',
  1196. markDirty : true,
  1197. /**
  1198. * @cfg {Number} cellSelectorDepth The number of levels to search for cells in event delegation (defaults to <tt>4</tt>)
  1199. */
  1200. cellSelectorDepth : 4,
  1201. /**
  1202. * @cfg {Number} rowSelectorDepth The number of levels to search for rows in event delegation (defaults to <tt>10</tt>)
  1203. */
  1204. rowSelectorDepth : 10,
  1205. /**
  1206. * @cfg {Number} rowBodySelectorDepth The number of levels to search for row bodies in event delegation (defaults to <tt>10</tt>)
  1207. */
  1208. rowBodySelectorDepth : 10,
  1209. /**
  1210. * @cfg {String} cellSelector The selector used to find cells internally (defaults to <tt>'td.x-grid3-cell'</tt>)
  1211. */
  1212. cellSelector : 'td.x-grid3-cell',
  1213. /**
  1214. * @cfg {String} rowSelector The selector used to find rows internally (defaults to <tt>'div.x-grid3-row'</tt>)
  1215. */
  1216. rowSelector : 'div.x-grid3-row',
  1217. /**
  1218. * @cfg {String} rowBodySelector The selector used to find row bodies internally (defaults to <tt>'div.x-grid3-row'</tt>)
  1219. */
  1220. rowBodySelector : 'div.x-grid3-row-body',
  1221. // private
  1222. firstRowCls: 'x-grid3-row-first',
  1223. lastRowCls: 'x-grid3-row-last',
  1224. rowClsRe: /(?:^|\s+)x-grid3-row-(first|last|alt)(?:\s+|$)/g,
  1225. constructor : function(config){
  1226. Ext.apply(this, config);
  1227. // These events are only used internally by the grid components
  1228. this.addEvents(
  1229. /**
  1230. * @event beforerowremoved
  1231. * Internal UI Event. Fired before a row is removed.
  1232. * @param {Ext.grid.GridView} view
  1233. * @param {Number} rowIndex The index of the row to be removed.
  1234. * @param {Ext.data.Record} record The Record to be removed
  1235. */
  1236. 'beforerowremoved',
  1237. /**
  1238. * @event beforerowsinserted
  1239. * Internal UI Event. Fired before rows are inserted.
  1240. * @param {Ext.grid.GridView} view
  1241. * @param {Number} firstRow The index of the first row to be inserted.
  1242. * @param {Number} lastRow The index of the last row to be inserted.
  1243. */
  1244. 'beforerowsinserted',
  1245. /**
  1246. * @event beforerefresh
  1247. * Internal UI Event. Fired before the view is refreshed.
  1248. * @param {Ext.grid.GridView} view
  1249. */
  1250. 'beforerefresh',
  1251. /**
  1252. * @event rowremoved
  1253. * Internal UI Event. Fired after a row is removed.
  1254. * @param {Ext.grid.GridView} view
  1255. * @param {Number} rowIndex The index of the row that was removed.
  1256. * @param {Ext.data.Record} record The Record that was removed
  1257. */
  1258. 'rowremoved',
  1259. /**
  1260. * @event rowsinserted
  1261. * Internal UI Event. Fired after rows are inserted.
  1262. * @param {Ext.grid.GridView} view
  1263. * @param {Number} firstRow The index of the first inserted.
  1264. * @param {Number} lastRow The index of the last row inserted.
  1265. */
  1266. 'rowsinserted',
  1267. /**
  1268. * @event rowupdated
  1269. * Internal UI Event. Fired after a row has been updated.
  1270. * @param {Ext.grid.GridView} view
  1271. * @param {Number} firstRow The index of the row updated.
  1272. * @param {Ext.data.record} record The Record backing the row updated.
  1273. */
  1274. 'rowupdated',
  1275. /**
  1276. * @event refresh
  1277. * Internal UI Event. Fired after the GridView's body has been refreshed.
  1278. * @param {Ext.grid.GridView} view
  1279. */
  1280. 'refresh'
  1281. );
  1282. Ext.grid.GridView.superclass.constructor.call(this);
  1283. },
  1284. /* -------------------------------- UI Specific ----------------------------- */
  1285. // private
  1286. initTemplates : function(){
  1287. var ts = this.templates || {};
  1288. if(!ts.master){
  1289. ts.master = new Ext.Template(
  1290. '<div class="x-grid3" hidefocus="true">',
  1291. '<div class="x-grid3-viewport">',
  1292. '<div class="x-grid3-header"><div class="x-grid3-header-inner"><div class="x-grid3-header-offset" style="{ostyle}">{header}</div></div><div class="x-clear"></div></div>',
  1293. '<div class="x-grid3-scroller"><div class="x-grid3-body" style="{bstyle}">{body}</div><a href="#" class="x-grid3-focus" tabIndex="-1"></a></div>',
  1294. '</div>',
  1295. '<div class="x-grid3-resize-marker">&#160;</div>',
  1296. '<div class="x-grid3-resize-proxy">&#160;</div>',
  1297. '</div>'
  1298. );
  1299. }
  1300. if(!ts.header){
  1301. ts.header = new Ext.Template(
  1302. '<table border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
  1303. '<thead><tr class="x-grid3-hd-row">{cells}</tr></thead>',
  1304. '</table>'
  1305. );
  1306. }
  1307. if(!ts.hcell){
  1308. ts.hcell = new Ext.Template(
  1309. '<td class="x-grid3-hd x-grid3-cell x-grid3-td-{id} {css}" style="{style}"><div {tooltip} {attr} class="x-grid3-hd-inner x-grid3-hd-{id}" unselectable="on" style="{istyle}">', this.grid.enableHdMenu ? '<a class="x-grid3-hd-btn" href="#"></a>' : '',
  1310. '{value}<img class="x-grid3-sort-icon" src="', Ext.BLANK_IMAGE_URL, '" />',
  1311. '</div></td>'
  1312. );
  1313. }
  1314. if(!ts.body){
  1315. ts.body = new Ext.Template('{rows}');
  1316. }
  1317. if(!ts.row){
  1318. ts.row = new Ext.Template(
  1319. '<div class="x-grid3-row {alt}" style="{tstyle}"><table class="x-grid3-row-table" border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
  1320. '<tbody><tr>{cells}</tr>',
  1321. (this.enableRowBody ? '<tr class="x-grid3-row-body-tr" style="{bodyStyle}"><td colspan="{cols}" class="x-grid3-body-cell" tabIndex="0" hidefocus="on"><div class="x-grid3-row-body">{body}</div></td></tr>' : ''),
  1322. '</tbody></table></div>'
  1323. );
  1324. }
  1325. if(!ts.cell){
  1326. ts.cell = new Ext.Template(
  1327. '<td class="x-grid3-col x-grid3-cell x-grid3-td-{id} {css}" style="{style}" tabIndex="0" {cellAttr}>',
  1328. '<div class="x-grid3-cell-inner x-grid3-col-{id}" unselectable="on" {attr}>{value}</div>',
  1329. '</td>'
  1330. );
  1331. }
  1332. for(var k in ts){
  1333. var t = ts[k];
  1334. if(t && Ext.isFunction(t.compile) && !t.compiled){
  1335. t.disableFormats = true;
  1336. t.compile();
  1337. }
  1338. }
  1339. this.templates = ts;
  1340. this.colRe = new RegExp('x-grid3-td-([^\\s]+)', '');
  1341. },
  1342. // private
  1343. fly : function(el){
  1344. if(!this._flyweight){
  1345. this._flyweight = new Ext.Element.Flyweight(document.body);
  1346. }
  1347. this._flyweight.dom = el;
  1348. return this._flyweight;
  1349. },
  1350. // private
  1351. getEditorParent : function(){
  1352. return this.scroller.dom;
  1353. },
  1354. // private
  1355. initElements : function(){
  1356. var E = Ext.Element;
  1357. var el = this.grid.getGridEl().dom.firstChild;
  1358. var cs = el.childNodes;
  1359. this.el = new E(el);
  1360. this.mainWrap = new E(cs[0]);
  1361. this.mainHd = new E(this.mainWrap.dom.firstChild);
  1362. if(this.grid.hideHeaders){
  1363. this.mainHd.setDisplayed(false);
  1364. }
  1365. this.innerHd = this.mainHd.dom.firstChild;
  1366. this.scroller = new E(this.mainWrap.dom.childNodes[1]);
  1367. if(this.forceFit){
  1368. this.scroller.setStyle('overflow-x', 'hidden');
  1369. }
  1370. /**
  1371. * <i>Read-only</i>. The GridView's body Element which encapsulates all rows in the Grid.
  1372. * This {@link Ext.Element Element} is only available after the GridPanel has been rendered.
  1373. * @type Ext.Element
  1374. * @property mainBody
  1375. */
  1376. this.mainBody = new E(this.scroller.dom.firstChild);
  1377. this.focusEl = new E(this.scroller.dom.childNodes[1]);
  1378. this.focusEl.swallowEvent('click', true);
  1379. this.resizeMarker = new E(cs[1]);
  1380. this.resizeProxy = new E(cs[2]);
  1381. },
  1382. // private
  1383. getRows : function(){
  1384. return this.hasRows() ? this.mainBody.dom.childNodes : [];
  1385. },
  1386. // finder methods, used with delegation
  1387. // private
  1388. findCell : function(el){
  1389. if(!el){
  1390. return false;
  1391. }
  1392. return this.fly(el).findParent(this.cellSelector, this.cellSelectorDepth);
  1393. },
  1394. /**
  1395. * <p>Return the index of the grid column which contains the passed HTMLElement.</p>
  1396. * See also {@link #findRowIndex}
  1397. * @param {HTMLElement} el The target element
  1398. * @return {Number} The column index, or <b>false</b> if the target element is not within a row of this GridView.
  1399. */
  1400. findCellIndex : function(el, requiredCls){
  1401. var cell = this.findCell(el);
  1402. if(cell && (!requiredCls || this.fly(cell).hasClass(requiredCls))){
  1403. return this.getCellIndex(cell);
  1404. }
  1405. return false;
  1406. },
  1407. // private
  1408. getCellIndex : function(el){
  1409. if(el){
  1410. var m = el.className.match(this.colRe);
  1411. if(m && m[1]){
  1412. return this.cm.getIndexById(m[1]);
  1413. }
  1414. }
  1415. return false;
  1416. },
  1417. // private
  1418. findHeaderCell : function(el){
  1419. var cell = this.findCell(el);
  1420. return cell && this.fly(cell).hasClass(this.hdCls) ? cell : null;
  1421. },
  1422. // private
  1423. findHeaderIndex : function(el){
  1424. return this.findCellIndex(el, this.hdCls);
  1425. },
  1426. /**
  1427. * Return the HtmlElement representing the grid row which contains the passed element.
  1428. * @param {HTMLElement} el The target HTMLElement
  1429. * @return {HTMLElement} The row element, or null if the target element is not within a row of this GridView.
  1430. */
  1431. findRow : function(el){
  1432. if(!el){
  1433. return false;
  1434. }
  1435. return this.fly(el).findParent(this.rowSelector, this.rowSelectorDepth);
  1436. },
  1437. /**
  1438. * <p>Return the index of the grid row which contains the passed HTMLElement.</p>
  1439. * See also {@link #findCellIndex}
  1440. * @param {HTMLElement} el The target HTMLElement
  1441. * @return {Number} The row index, or <b>false</b> if the target element is not within a row of this GridView.
  1442. */
  1443. findRowIndex : function(el){
  1444. var r = this.findRow(el);
  1445. return r ? r.rowIndex : false;
  1446. },
  1447. /**
  1448. * Return the HtmlElement representing the grid row body which contains the passed element.
  1449. * @param {HTMLElement} el The target HTMLElement
  1450. * @return {HTMLElement} The row body element, or null if the target element is not within a row body of this GridView.
  1451. */
  1452. findRowBody : function(el){
  1453. if(!el){
  1454. return false;
  1455. }
  1456. return this.fly(el).findParent(this.rowBodySelector, this.rowBodySelectorDepth);
  1457. },
  1458. // getter methods for fetching elements dynamically in the grid
  1459. /**
  1460. * Return the <tt>&lt;div></tt> HtmlElement which represents a Grid row for the specified index.
  1461. * @param {Number} index The row index
  1462. * @return {HtmlElement} The div element.
  1463. */
  1464. getRow : function(row){
  1465. return this.getRows()[row];
  1466. },
  1467. /**
  1468. * Returns the grid's <tt>&lt;td></tt> HtmlElement at the specified coordinates.
  1469. * @param {Number} row The row index in which to find the cell.
  1470. * @param {Number} col The column index of the cell.
  1471. * @return {HtmlElement} The td at the specified coordinates.
  1472. */
  1473. getCell : function(row, col){
  1474. return this.getRow(row).getElementsByTagName('td')[col];
  1475. },
  1476. /**
  1477. * Return the <tt>&lt;td></tt> HtmlElement which represents the Grid's header cell for the specified column index.
  1478. * @param {Number} index The column index
  1479. * @return {HtmlElement} The td element.
  1480. */
  1481. getHeaderCell : function(index){
  1482. return this.mainHd.dom.getElementsByTagName('td')[index];
  1483. },
  1484. // manipulating elements
  1485. // private - use getRowClass to apply custom row classes
  1486. addRowClass : function(row, cls){
  1487. var r = this.getRow(row);
  1488. if(r){
  1489. this.fly(r).addClass(cls);
  1490. }
  1491. },
  1492. // private
  1493. removeRowClass : function(row, cls){
  1494. var r = this.getRow(row);
  1495. if(r){
  1496. this.fly(r).removeClass(cls);
  1497. }
  1498. },
  1499. // private
  1500. removeRow : function(row){
  1501. Ext.removeNode(this.getRow(row));
  1502. this.syncFocusEl(row);
  1503. },
  1504. // private
  1505. removeRows : function(firstRow, lastRow){
  1506. var bd = this.mainBody.dom;
  1507. for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
  1508. Ext.removeNode(bd.childNodes[firstRow]);
  1509. }
  1510. this.syncFocusEl(firstRow);
  1511. },
  1512. // scrolling stuff
  1513. // private
  1514. getScrollState : function(){
  1515. var sb = this.scroller.dom;
  1516. return {left: sb.scrollLeft, top: sb.scrollTop};
  1517. },
  1518. // private
  1519. restoreScroll : function(state){
  1520. var sb = this.scroller.dom;
  1521. sb.scrollLeft = state.left;
  1522. sb.scrollTop = state.top;
  1523. },
  1524. /**
  1525. * Scrolls the grid to the top
  1526. */
  1527. scrollToTop : function(){
  1528. this.scroller.dom.scrollTop = 0;
  1529. this.scroller.dom.scrollLeft = 0;
  1530. },
  1531. // private
  1532. syncScroll : function(){
  1533. this.syncHeaderScroll();
  1534. var mb = this.scroller.dom;
  1535. this.grid.fireEvent('bodyscroll', mb.scrollLeft, mb.scrollTop);
  1536. },
  1537. // private
  1538. syncHeaderScroll : function(){
  1539. var mb = this.scroller.dom;
  1540. this.innerHd.scrollLeft = mb.scrollLeft;
  1541. this.innerHd.scrollLeft = mb.scrollLeft; // second time for IE (1/2 time first fails, other browsers ignore)
  1542. },
  1543. // private
  1544. updateSortIcon : function(col, dir){
  1545. var sc = this.sortClasses;
  1546. var hds = this.mainHd.select('td').removeClass(sc);
  1547. hds.item(col).addClass(sc[dir == 'DESC' ? 1 : 0]);
  1548. },
  1549. // private
  1550. updateAllColumnWidths : function(){
  1551. var tw = this.getTotalWidth(),
  1552. clen = this.cm.getColumnCount(),
  1553. ws = [],
  1554. len,
  1555. i;
  1556. for(i = 0; i < clen; i++){
  1557. ws[i] = this.getColumnWidth(i);
  1558. }
  1559. this.innerHd.firstChild.style.width = this.getOffsetWidth();
  1560. this.innerHd.firstChild.firstChild.style.width = tw;
  1561. this.mainBody.dom.style.width = tw;
  1562. for(i = 0; i < clen; i++){
  1563. var hd = this.getHeaderCell(i);
  1564. hd.style.width = ws[i];
  1565. }
  1566. var ns = this.getRows(), row, trow;
  1567. for(i = 0, len = ns.length; i < len; i++){
  1568. row = ns[i];
  1569. row.style.width = tw;
  1570. if(row.firstChild){
  1571. row.firstChild.style.width = tw;
  1572. trow = row.firstChild.rows[0];
  1573. for (var j = 0; j < clen; j++) {
  1574. trow.childNodes[j].style.width = ws[j];
  1575. }
  1576. }
  1577. }
  1578. this.onAllColumnWidthsUpdated(ws, tw);
  1579. },
  1580. // private
  1581. updateColumnWidth : function(col, width){
  1582. var w = this.getColumnWidth(col);
  1583. var tw = this.getTotalWidth();
  1584. this.innerHd.firstChild.style.width = this.getOffsetWidth();
  1585. this.innerHd.firstChild.firstChild.style.width = tw;
  1586. this.mainBody.dom.style.width = tw;
  1587. var hd = this.getHeaderCell(col);
  1588. hd.style.width = w;
  1589. var ns = this.getRows(), row;
  1590. for(var i = 0, len = ns.length; i < len; i++){
  1591. row = ns[i];
  1592. row.style.width = tw;
  1593. if(row.firstChild){
  1594. row.firstChild.style.width = tw;
  1595. row.firstChild.rows[0].childNodes[col].style.width = w;
  1596. }
  1597. }
  1598. this.onColumnWidthUpdated(col, w, tw);
  1599. },
  1600. // private
  1601. updateColumnHidden : function(col, hidden){
  1602. var tw = this.getTotalWidth();
  1603. this.innerHd.firstChild.style.width = this.getOffsetWidth();
  1604. this.innerHd.firstChild.firstChild.style.width = tw;
  1605. this.mainBody.dom.style.width = tw;
  1606. var display = hidden ? 'none' : '';
  1607. var hd = this.getHeaderCell(col);
  1608. hd.style.display = display;
  1609. var ns = this.getRows(), row;
  1610. for(var i = 0, len = ns.length; i < len; i++){
  1611. row = ns[i];
  1612. row.style.width = tw;
  1613. if(row.firstChild){
  1614. row.firstChild.style.width = tw;
  1615. row.firstChild.rows[0].childNodes[col].style.display = display;
  1616. }
  1617. }
  1618. this.onColumnHiddenUpdated(col, hidden, tw);
  1619. delete this.lastViewWidth; // force recalc
  1620. this.layout();
  1621. },
  1622. /**
  1623. * @private
  1624. * Renders all of the rows to a string buffer and returns the string. This is called internally
  1625. * by renderRows and performs the actual string building for the rows - it does not inject HTML into the DOM.
  1626. * @param {Array} columns The column data acquired from getColumnData.
  1627. * @param {Array} records The array of records to render
  1628. * @param {Ext.data.Store} store The store to render the rows from
  1629. * @param {Number} startRow The index of the first row being rendered. Sometimes we only render a subset of
  1630. * the rows so this is used to maintain logic for striping etc
  1631. * @param {Number} colCount The total number of columns in the column model
  1632. * @param {Boolean} stripe True to stripe the rows
  1633. * @return {String} A string containing the HTML for the rendered rows
  1634. */
  1635. doRender : function(columns, records, store, startRow, colCount, stripe) {
  1636. var templates = this.templates,
  1637. cellTemplate = templates.cell,
  1638. rowTemplate = templates.row,
  1639. last = colCount - 1;
  1640. var tstyle = 'width:' + this.getTotalWidth() + ';';
  1641. // buffers
  1642. var rowBuffer = [],
  1643. colBuffer = [],
  1644. rowParams = {tstyle: tstyle},
  1645. meta = {},
  1646. column,
  1647. record;
  1648. //build up each row's HTML
  1649. for (var j = 0, len = records.length; j < len; j++) {
  1650. record = records[j];
  1651. colBuffer = [];
  1652. var rowIndex = j + startRow;
  1653. //build up each column's HTML
  1654. for (var i = 0; i < colCount; i++) {
  1655. column = columns[i];
  1656. meta.id = column.id;
  1657. meta.css = i === 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '');
  1658. meta.attr = meta.cellAttr = '';
  1659. meta.style = column.style;
  1660. meta.value = column.renderer.call(column.scope, record.data[column.name], meta, record, rowIndex, i, store);
  1661. if (Ext.isEmpty(meta.value)) {
  1662. meta.value = '&#160;';
  1663. }
  1664. if (this.markDirty && record.dirty && Ext.isDefined(record.modified[column.name])) {
  1665. meta.css += ' x-grid3-dirty-cell';
  1666. }
  1667. colBuffer[colBuffer.length] = cellTemplate.apply(meta);
  1668. }
  1669. //set up row striping and row dirtiness CSS classes
  1670. var alt = [];
  1671. if (stripe && ((rowIndex + 1) % 2 === 0)) {
  1672. alt[0] = 'x-grid3-row-alt';
  1673. }
  1674. if (record.dirty) {
  1675. alt[1] = ' x-grid3-dirty-row';
  1676. }
  1677. rowParams.cols = colCount;
  1678. if (this.getRowClass) {
  1679. alt[2] = this.getRowClass(record, rowIndex, rowParams, store);
  1680. }
  1681. rowParams.alt = alt.join(' ');
  1682. rowParams.cells = colBuffer.join('');
  1683. rowBuffer[rowBuffer.length] = rowTemplate.apply(rowParams);
  1684. }
  1685. return rowBuffer.join('');
  1686. },
  1687. // private
  1688. processRows : function(startRow, skipStripe) {
  1689. if (!this.ds || this.ds.getCount() < 1) {
  1690. return;
  1691. }
  1692. var rows = this.getRows(),
  1693. len = rows.length,
  1694. i, r;
  1695. skipStripe = skipStripe || !this.grid.stripeRows;
  1696. startRow = startRow || 0;
  1697. for (i = 0; i<len; i++) {
  1698. r = rows[i];
  1699. if (r) {
  1700. r.rowIndex = i;
  1701. if (!skipStripe) {
  1702. r.className = r.className.replace(this.rowClsRe, ' ');
  1703. if ((i + 1) % 2 === 0){
  1704. r.className += ' x-grid3-row-alt';
  1705. }
  1706. }
  1707. }
  1708. }
  1709. // add first/last-row classes
  1710. if (startRow === 0) {
  1711. Ext.fly(rows[0]).addClass(this.firstRowCls);
  1712. }
  1713. Ext.fly(rows[rows.length - 1]).addClass(this.lastRowCls);
  1714. },
  1715. afterRender : function(){
  1716. if(!this.ds || !this.cm){
  1717. return;
  1718. }
  1719. this.mainBody.dom.innerHTML = this.renderRows() || '&#160;';
  1720. this.processRows(0, true);
  1721. if(this.deferEmptyText !== true){
  1722. this.applyEmptyText();
  1723. }
  1724. this.grid.fireEvent('viewready', this.grid);
  1725. },
  1726. /**
  1727. * @private
  1728. * Renders each of the UI elements in turn. This is called internally, once, by this.render. It does not
  1729. * render rows from the store, just the surrounding UI elements. It also sets up listeners on the UI elements
  1730. * and sets up options like column menus, moving and resizing.
  1731. */
  1732. renderUI : function() {
  1733. var templates = this.templates,
  1734. header = this.renderHeaders(),
  1735. body = templates.body.apply({rows:'&#160;'});
  1736. var html = templates.master.apply({
  1737. body : body,
  1738. header: header,
  1739. ostyle: 'width:' + this.getOffsetWidth() + ';',
  1740. bstyle: 'width:' + this.getTotalWidth() + ';'
  1741. });
  1742. var g = this.grid;
  1743. g.getGridEl().dom.innerHTML = html;
  1744. this.initElements();
  1745. // get mousedowns early
  1746. Ext.fly(this.innerHd).on('click', this.handleHdDown, this);
  1747. this.mainHd.on({
  1748. scope : this,
  1749. mouseover: this.handleHdOver,
  1750. mouseout : this.handleHdOut,
  1751. mousemove: this.handleHdMove
  1752. });
  1753. this.scroller.on('scroll', this.syncScroll, this);
  1754. if (g.enableColumnResize !== false) {
  1755. this.splitZone = new Ext.grid.GridView.SplitDragZone(g, this.mainHd.dom);
  1756. }
  1757. if (g.enableColumnMove) {
  1758. this.columnDrag = new Ext.grid.GridView.ColumnDragZone(g, this.innerHd);
  1759. this.columnDrop = new Ext.grid.HeaderDropZone(g, this.mainHd.dom);
  1760. }
  1761. if (g.enableHdMenu !== false) {
  1762. this.hmenu = new Ext.menu.Menu({id: g.id + '-hctx'});
  1763. this.hmenu.add(
  1764. {itemId:'asc', text: this.sortAscText, cls: 'xg-hmenu-sort-asc'},
  1765. {itemId:'desc', text: this.sortDescText, cls: 'xg-hmenu-sort-desc'}
  1766. );
  1767. if (g.enableColumnHide !== false) {
  1768. this.colMenu = new Ext.menu.Menu({id:g.id + '-hcols-menu'});
  1769. this.colMenu.on({
  1770. scope : this,
  1771. beforeshow: this.beforeColMenuShow,
  1772. itemclick : this.handleHdMenuClick
  1773. });
  1774. this.hmenu.add('-', {
  1775. itemId:'columns',
  1776. hideOnClick: false,
  1777. text: this.columnsText,
  1778. menu: this.colMenu,
  1779. iconCls: 'x-cols-icon'
  1780. });
  1781. }
  1782. this.hmenu.on('itemclick', this.handleHdMenuClick, this);
  1783. }
  1784. if (g.trackMouseOver) {
  1785. this.mainBody.on({
  1786. scope : this,
  1787. mouseover: this.onRowOver,
  1788. mouseout : this.onRowOut
  1789. });
  1790. }
  1791. if (g.enableDragDrop || g.enableDrag) {
  1792. this.dragZone = new Ext.grid.GridDragZone(g, {
  1793. ddGroup : g.ddGroup || 'GridDD'
  1794. });
  1795. }
  1796. this.updateHeaderSortState();
  1797. },
  1798. // private
  1799. processEvent : function(name, e) {
  1800. var t = e.getTarget(),
  1801. g = this.grid,
  1802. header = this.findHeaderIndex(t);
  1803. g.fireEvent(name, e);
  1804. if (header !== false) {
  1805. g.fireEvent('header' + name, g, header, e);
  1806. } else {
  1807. var row = this.findRowIndex(t),
  1808. cell,
  1809. body;
  1810. if (row !== false) {
  1811. g.fireEvent('row' + name, g, row, e);
  1812. cell = this.findCellIndex(t);
  1813. if (cell !== false) {
  1814. g.fireEvent('cell' + name, g, row, cell, e);
  1815. } else {
  1816. body = this.findRowBody(t);
  1817. if (body) {
  1818. g.fireEvent('rowbody' + name, g, row, e);
  1819. }
  1820. }
  1821. } else {
  1822. g.fireEvent('container' + name, g, e);
  1823. }
  1824. }
  1825. },
  1826. // private
  1827. layout : function() {
  1828. if(!this.mainBody){
  1829. return; // not rendered
  1830. }
  1831. var g = this.grid;
  1832. var c = g.getGridEl();
  1833. var csize = c.getSize(true);
  1834. var vw = csize.width;
  1835. if(!g.hideHeaders && (vw < 20 || csize.height < 20)){ // display: none?
  1836. return;
  1837. }
  1838. if(g.autoHeight){
  1839. this.scroller.dom.style.overflow = 'visible';
  1840. if(Ext.isWebKit){
  1841. this.scroller.dom.style.position = 'static';
  1842. }
  1843. }else{
  1844. this.el.setSize(csize.width, csize.height);
  1845. var hdHeight = this.mainHd.getHeight();
  1846. var vh = csize.height - (hdHeight);
  1847. this.scroller.setSize(vw, vh);
  1848. if(this.innerHd){
  1849. this.innerHd.style.width = (vw)+'px';
  1850. }
  1851. }
  1852. if(this.forceFit){
  1853. if(this.lastViewWidth != vw){
  1854. this.fitColumns(false, false);
  1855. this.lastViewWidth = vw;
  1856. }
  1857. }else {
  1858. this.autoExpand();
  1859. this.syncHeaderScroll();
  1860. }
  1861. this.onLayout(vw, vh);
  1862. },
  1863. // template functions for subclasses and plugins
  1864. // these functions include precalculated values
  1865. onLayout : function(vw, vh){
  1866. // do nothing
  1867. },
  1868. onColumnWidthUpdated : function(col, w, tw){
  1869. //template method
  1870. },
  1871. onAllColumnWidthsUpdated : function(ws, tw){
  1872. //template method
  1873. },
  1874. onColumnHiddenUpdated : function(col, hidden, tw){
  1875. // template method
  1876. },
  1877. updateColumnText : function(col, text){
  1878. // template method
  1879. },
  1880. afterMove : function(colIndex){
  1881. // template method
  1882. },
  1883. /* ----------------------------------- Core Specific -------------------------------------------*/
  1884. // private
  1885. init : function(grid){
  1886. this.grid = grid;
  1887. this.initTemplates();
  1888. this.initData(grid.store, grid.colModel);
  1889. this.initUI(grid);
  1890. },
  1891. // private
  1892. getColumnId : function(index){
  1893. return this.cm.getColumnId(index);
  1894. },
  1895. // private
  1896. getOffsetWidth : function() {
  1897. return (this.cm.getTotalWidth() + this.getScrollOffset()) + 'px';
  1898. },
  1899. getScrollOffset: function(){
  1900. return Ext.num(this.scrollOffset, Ext.getScrollBarWidth());
  1901. },
  1902. /**
  1903. * @private
  1904. * Renders the header row using the 'header' template. Does not inject the HTML into the DOM, just
  1905. * returns a string.
  1906. * @return {String} Rendered header row
  1907. */
  1908. renderHeaders : function() {
  1909. var cm = this.cm,
  1910. ts = this.templates,
  1911. ct = ts.hcell,
  1912. cb = [],
  1913. p = {},
  1914. len = cm.getColumnCount(),
  1915. last = len - 1;
  1916. for (var i = 0; i < len; i++) {
  1917. p.id = cm.getColumnId(i);
  1918. p.value = cm.getColumnHeader(i) || '';
  1919. p.style = this.getColumnStyle(i, true);
  1920. p.tooltip = this.getColumnTooltip(i);
  1921. p.css = i === 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '');
  1922. if (cm.config[i].align == 'right') {
  1923. p.istyle = 'padding-right:16px';
  1924. } else {
  1925. delete p.istyle;
  1926. }
  1927. cb[cb.length] = ct.apply(p);
  1928. }
  1929. return ts.header.apply({cells: cb.join(''), tstyle:'width:'+this.getTotalWidth()+';'});
  1930. },
  1931. // private
  1932. getColumnTooltip : function(i){
  1933. var tt = this.cm.getColumnTooltip(i);
  1934. if(tt){
  1935. if(Ext.QuickTips.isEnabled()){
  1936. return 'ext:qtip="'+tt+'"';
  1937. }else{
  1938. return 'title="'+tt+'"';
  1939. }
  1940. }
  1941. return '';
  1942. },
  1943. // private
  1944. beforeUpdate : function(){
  1945. this.grid.stopEditing(true);
  1946. },
  1947. // private
  1948. updateHeaders : function(){
  1949. this.innerHd.firstChild.innerHTML = this.renderHeaders();
  1950. this.innerHd.firstChild.style.width = this.getOffsetWidth();
  1951. this.innerHd.firstChild.firstChild.style.width = this.getTotalWidth();
  1952. },
  1953. /**
  1954. * Focuses the specified row.
  1955. * @param {Number} row The row index
  1956. */
  1957. focusRow : function(row){
  1958. this.focusCell(row, 0, false);
  1959. },
  1960. /**
  1961. * Focuses the specified cell.
  1962. * @param {Number} row The row index
  1963. * @param {Number} col The column index
  1964. */
  1965. focusCell : function(row, col, hscroll){
  1966. this.syncFocusEl(this.ensureVisible(row, col, hscroll));
  1967. if(Ext.isGecko){
  1968. this.focusEl.focus();
  1969. }else{
  1970. this.focusEl.focus.defer(1, this.focusEl);
  1971. }
  1972. },
  1973. resolveCell : function(row, col, hscroll){
  1974. if(!Ext.isNumber(row)){
  1975. row = row.rowIndex;
  1976. }
  1977. if(!this.ds){
  1978. return null;
  1979. }
  1980. if(row < 0 || row >= this.ds.getCount()){
  1981. return null;
  1982. }
  1983. col = (col !== undefined ? col : 0);
  1984. var rowEl = this.getRow(row),
  1985. cm = this.cm,
  1986. colCount = cm.getColumnCount(),
  1987. cellEl;
  1988. if(!(hscroll === false && col === 0)){
  1989. while(col < colCount && cm.isHidden(col)){
  1990. col++;
  1991. }
  1992. cellEl = this.getCell(row, col);
  1993. }
  1994. return {row: rowEl, cell: cellEl};
  1995. },
  1996. getResolvedXY : function(resolved){
  1997. if(!resolved){
  1998. return null;
  1999. }
  2000. var s = this.scroller.dom, c = resolved.cell, r = resolved.row;
  2001. return c ? Ext.fly(c).getXY() : [this.el.getX(), Ext.fly(r).getY()];
  2002. },
  2003. syncFocusEl : function(row, col, hscroll){
  2004. var xy = row;
  2005. if(!Ext.isArray(xy)){
  2006. row = Math.min(row, Math.max(0, this.getRows().length-1));
  2007. if (isNaN(row)) {
  2008. return;
  2009. }
  2010. xy = this.getResolvedXY(this.resolveCell(row, col, hscroll));
  2011. }
  2012. this.focusEl.setXY(xy||this.scroller.getXY());
  2013. },
  2014. ensureVisible : function(row, col, hscroll){
  2015. var resolved = this.resolveCell(row, col, hscroll);
  2016. if(!resolved || !resolved.row){
  2017. return;
  2018. }
  2019. var rowEl = resolved.row,
  2020. cellEl = resolved.cell,
  2021. c = this.scroller.dom,
  2022. ctop = 0,
  2023. p = rowEl,
  2024. stop = this.el.dom;
  2025. while(p && p != stop){
  2026. ctop += p.offsetTop;
  2027. p = p.offsetParent;
  2028. }
  2029. ctop -= this.mainHd.dom.offsetHeight;
  2030. stop = parseInt(c.scrollTop, 10);
  2031. var cbot = ctop + rowEl.offsetHeight,
  2032. ch = c.clientHeight,
  2033. sbot = stop + ch;
  2034. if(ctop < stop){
  2035. c.scrollTop = ctop;
  2036. }else if(cbot > sbot){
  2037. c.scrollTop = cbot-ch;
  2038. }
  2039. if(hscroll !== false){
  2040. var cleft = parseInt(cellEl.offsetLeft, 10);
  2041. var cright = cleft + cellEl.offsetWidth;
  2042. var sleft = parseInt(c.scrollLeft, 10);
  2043. var sright = sleft + c.clientWidth;
  2044. if(cleft < sleft){
  2045. c.scrollLeft = cleft;
  2046. }else if(cright > sright){
  2047. c.scrollLeft = cright-c.clientWidth;
  2048. }
  2049. }
  2050. return this.getResolvedXY(resolved);
  2051. },
  2052. // private
  2053. insertRows : function(dm, firstRow, lastRow, isUpdate) {
  2054. var last = dm.getCount() - 1;
  2055. if( !isUpdate && firstRow === 0 && lastRow >= last) {
  2056. this.fireEvent('beforerowsinserted', this, firstRow, lastRow);
  2057. this.refresh();
  2058. this.fireEvent('rowsinserted', this, firstRow, lastRow);
  2059. } else {
  2060. if (!isUpdate) {
  2061. this.fireEvent('beforerowsinserted', this, firstRow, lastRow);
  2062. }
  2063. var html = this.renderRows(firstRow, lastRow),
  2064. before = this.getRow(firstRow);
  2065. if (before) {
  2066. if(firstRow === 0){
  2067. Ext.fly(this.getRow(0)).removeClass(this.firstRowCls);
  2068. }
  2069. Ext.DomHelper.insertHtml('beforeBegin', before, html);
  2070. } else {
  2071. var r = this.getRow(last - 1);
  2072. if(r){
  2073. Ext.fly(r).removeClass(this.lastRowCls);
  2074. }
  2075. Ext.DomHelper.insertHtml('beforeEnd', this.mainBody.dom, html);
  2076. }
  2077. if (!isUpdate) {
  2078. this.fireEvent('rowsinserted', this, firstRow, lastRow);
  2079. this.processRows(firstRow);
  2080. } else if (firstRow === 0 || firstRow >= last) {
  2081. //ensure first/last row is kept after an update.
  2082. Ext.fly(this.getRow(firstRow)).addClass(firstRow === 0 ? this.firstRowCls : this.lastRowCls);
  2083. }
  2084. }
  2085. this.syncFocusEl(firstRow);
  2086. },
  2087. // private
  2088. deleteRows : function(dm, firstRow, lastRow){
  2089. if(dm.getRowCount()<1){
  2090. this.refresh();
  2091. }else{
  2092. this.fireEvent('beforerowsdeleted', this, firstRow, lastRow);
  2093. this.removeRows(firstRow, lastRow);
  2094. this.processRows(firstRow);
  2095. this.fireEvent('rowsdeleted', this, firstRow, lastRow);
  2096. }
  2097. },
  2098. // private
  2099. getColumnStyle : function(col, isHeader){
  2100. var style = !isHeader ? (this.cm.config[col].css || '') : '';
  2101. style += 'width:'+this.getColumnWidth(col)+';';
  2102. if(this.cm.isHidden(col)){
  2103. style += 'display:none;';
  2104. }
  2105. var align = this.cm.config[col].align;
  2106. if(align){
  2107. style += 'text-align:'+align+';';
  2108. }
  2109. return style;
  2110. },
  2111. // private
  2112. getColumnWidth : function(col){
  2113. var w = this.cm.getColumnWidth(col);
  2114. if(Ext.isNumber(w)){
  2115. return (Ext.isBorderBox || (Ext.isWebKit && !Ext.isSafari2) ? w : (w - this.borderWidth > 0 ? w - this.borderWidth : 0)) + 'px';
  2116. }
  2117. return w;
  2118. },
  2119. // private
  2120. getTotalWidth : function(){
  2121. return this.cm.getTotalWidth()+'px';
  2122. },
  2123. // private
  2124. fitColumns : function(preventRefresh, onlyExpand, omitColumn){
  2125. var cm = this.cm, i;
  2126. var tw = cm.getTotalWidth(false);
  2127. var aw = this.grid.getGridEl().getWidth(true)-this.getScrollOffset();
  2128. if(aw < 20){ // not initialized, so don't screw up the default widths
  2129. return;
  2130. }
  2131. var extra = aw - tw;
  2132. if(extra === 0){
  2133. return false;
  2134. }
  2135. var vc = cm.getColumnCount(true);
  2136. var ac = vc-(Ext.isNumber(omitColumn) ? 1 : 0);
  2137. if(ac === 0){
  2138. ac = 1;
  2139. omitColumn = undefined;
  2140. }
  2141. var colCount = cm.getColumnCount();
  2142. var cols = [];
  2143. var extraCol = 0;
  2144. var width = 0;
  2145. var w;
  2146. for (i = 0; i < colCount; i++){
  2147. if(!cm.isHidden(i) && !cm.isFixed(i) && i !== omitColumn){
  2148. w = cm.getColumnWidth(i);
  2149. cols.push(i);
  2150. extraCol = i;
  2151. cols.push(w);
  2152. width += w;
  2153. }
  2154. }
  2155. var frac = (aw - cm.getTotalWidth())/width;
  2156. while (cols.length){
  2157. w = cols.pop();
  2158. i = cols.pop();
  2159. cm.setColumnWidth(i, Math.max(this.grid.minColumnWidth, Math.floor(w + w*frac)), true);
  2160. }
  2161. if((tw = cm.getTotalWidth(false)) > aw){
  2162. var adjustCol = ac != vc ? omitColumn : extraCol;
  2163. cm.setColumnWidth(adjustCol, Math.max(1,
  2164. cm.getColumnWidth(adjustCol)- (tw-aw)), true);
  2165. }
  2166. if(preventRefresh !== true){
  2167. this.updateAllColumnWidths();
  2168. }
  2169. return true;
  2170. },
  2171. // private
  2172. autoExpand : function(preventUpdate){
  2173. var g = this.grid, cm = this.cm;
  2174. if(!this.userResized && g.autoExpandColumn){
  2175. var tw = cm.getTotalWidth(false);
  2176. var aw = this.grid.getGridEl().getWidth(true)-this.getScrollOffset();
  2177. if(tw != aw){
  2178. var ci = cm.getIndexById(g.autoExpandColumn);
  2179. var currentWidth = cm.getColumnWidth(ci);
  2180. var cw = Math.min(Math.max(((aw-tw)+currentWidth), g.autoExpandMin), g.autoExpandMax);
  2181. if(cw != currentWidth){
  2182. cm.setColumnWidth(ci, cw, true);
  2183. if(preventUpdate !== true){
  2184. this.updateColumnWidth(ci, cw);
  2185. }
  2186. }
  2187. }
  2188. }
  2189. },
  2190. /**
  2191. * @private
  2192. * Returns an array of column configurations - one for each column
  2193. * @return {Array} Array of column config objects. This includes the column name, renderer, id style and renderer
  2194. */
  2195. getColumnData : function(){
  2196. // build a map for all the columns
  2197. var cs = [],
  2198. cm = this.cm,
  2199. colCount = cm.getColumnCount();
  2200. for (var i = 0; i < colCount; i++) {
  2201. var name = cm.getDataIndex(i);
  2202. cs[i] = {
  2203. name : (!Ext.isDefined(name) ? this.ds.fields.get(i).name : name),
  2204. renderer: cm.getRenderer(i),
  2205. scope : cm.getRendererScope(i),
  2206. id : cm.getColumnId(i),
  2207. style : this.getColumnStyle(i)
  2208. };
  2209. }
  2210. return cs;
  2211. },
  2212. // private
  2213. renderRows : function(startRow, endRow){
  2214. // pull in all the crap needed to render rows
  2215. var g = this.grid, cm = g.colModel, ds = g.store, stripe = g.stripeRows;
  2216. var colCount = cm.getColumnCount();
  2217. if(ds.getCount() < 1){
  2218. return '';
  2219. }
  2220. var cs = this.getColumnData();
  2221. startRow = startRow || 0;
  2222. endRow = !Ext.isDefined(endRow) ? ds.getCount()-1 : endRow;
  2223. // records to render
  2224. var rs = ds.getRange(startRow, endRow);
  2225. return this.doRender(cs, rs, ds, startRow, colCount, stripe);
  2226. },
  2227. // private
  2228. renderBody : function(){
  2229. var markup = this.renderRows() || '&#160;';
  2230. return this.templates.body.apply({rows: markup});
  2231. },
  2232. // private
  2233. refreshRow : function(record){
  2234. var ds = this.ds, index;
  2235. if(Ext.isNumber(record)){
  2236. index = record;
  2237. record = ds.getAt(index);
  2238. if(!record){
  2239. return;
  2240. }
  2241. }else{
  2242. index = ds.indexOf(record);
  2243. if(index < 0){
  2244. return;
  2245. }
  2246. }
  2247. this.insertRows(ds, index, index, true);
  2248. this.getRow(index).rowIndex = index;
  2249. this.onRemove(ds, record, index+1, true);
  2250. this.fireEvent('rowupdated', this, index, record);
  2251. },
  2252. /**
  2253. * Refreshs the grid UI
  2254. * @param {Boolean} headersToo (optional) True to also refresh the headers
  2255. */
  2256. refresh : function(headersToo){
  2257. this.fireEvent('beforerefresh', this);
  2258. this.grid.stopEditing(true);
  2259. var result = this.renderBody();
  2260. this.mainBody.update(result).setWidth(this.getTotalWidth());
  2261. if(headersToo === true){
  2262. this.updateHeaders();
  2263. this.updateHeaderSortState();
  2264. }
  2265. this.processRows(0, true);
  2266. this.layout();
  2267. this.applyEmptyText();
  2268. this.fireEvent('refresh', this);
  2269. },
  2270. /**
  2271. * @private
  2272. * Displays the configured emptyText if there are currently no rows to display
  2273. */
  2274. applyEmptyText : function(){
  2275. if(this.emptyText && !this.hasRows()){
  2276. this.mainBody.update('<div class="x-grid-empty">' + this.emptyText + '</div>');
  2277. }
  2278. },
  2279. /**
  2280. * @private
  2281. * Adds sorting classes to the column headers based on the bound store's sortInfo. Fires the 'sortchange' event
  2282. * if the sorting has changed since this function was last run.
  2283. */
  2284. updateHeaderSortState : function(){
  2285. var state = this.ds.getSortState();
  2286. if (!state) {
  2287. return;
  2288. }
  2289. if (!this.sortState || (this.sortState.field != state.field || this.sortState.direction != state.direction)) {
  2290. this.grid.fireEvent('sortchange', this.grid, state);
  2291. }
  2292. this.sortState = state;
  2293. var sortColumn = this.cm.findColumnIndex(state.field);
  2294. if (sortColumn != -1){
  2295. var sortDir = state.direction;
  2296. this.updateSortIcon(sortColumn, sortDir);
  2297. }
  2298. },
  2299. /**
  2300. * @private
  2301. * Removes any sorting indicator classes from the column headers
  2302. */
  2303. clearHeaderSortState : function(){
  2304. if (!this.sortState) {
  2305. return;
  2306. }
  2307. this.grid.fireEvent('sortchange', this.grid, null);
  2308. this.mainHd.select('td').removeClass(this.sortClasses);
  2309. delete this.sortState;
  2310. },
  2311. // private
  2312. destroy : function(){
  2313. if (this.scrollToTopTask && this.scrollToTopTask.cancel){
  2314. this.scrollToTopTask.cancel();
  2315. }
  2316. if(this.colMenu){
  2317. Ext.menu.MenuMgr.unregister(this.colMenu);
  2318. this.colMenu.destroy();
  2319. delete this.colMenu;
  2320. }
  2321. if(this.hmenu){
  2322. Ext.menu.MenuMgr.unregister(this.hmenu);
  2323. this.hmenu.destroy();
  2324. delete this.hmenu;
  2325. }
  2326. this.initData(null, null);
  2327. this.purgeListeners();
  2328. Ext.fly(this.innerHd).un("click", this.handleHdDown, this);
  2329. if(this.grid.enableColumnMove){
  2330. Ext.destroy(
  2331. this.columnDrag.el,
  2332. this.columnDrag.proxy.ghost,
  2333. this.columnDrag.proxy.el,
  2334. this.columnDrop.el,
  2335. this.columnDrop.proxyTop,
  2336. this.columnDrop.proxyBottom,
  2337. this.columnDrag.dragData.ddel,
  2338. this.columnDrag.dragData.header
  2339. );
  2340. if (this.columnDrag.proxy.anim) {
  2341. Ext.destroy(this.columnDrag.proxy.anim);
  2342. }
  2343. delete this.columnDrag.proxy.ghost;
  2344. delete this.columnDrag.dragData.ddel;
  2345. delete this.columnDrag.dragData.header;
  2346. this.columnDrag.destroy();
  2347. delete Ext.dd.DDM.locationCache[this.columnDrag.id];
  2348. delete this.columnDrag._domRef;
  2349. delete this.columnDrop.proxyTop;
  2350. delete this.columnDrop.proxyBottom;
  2351. this.columnDrop.destroy();
  2352. delete Ext.dd.DDM.locationCache["gridHeader" + this.grid.getGridEl().id];
  2353. delete this.columnDrop._domRef;
  2354. delete Ext.dd.DDM.ids[this.columnDrop.ddGroup];
  2355. }
  2356. if (this.splitZone){ // enableColumnResize
  2357. this.splitZone.destroy();
  2358. delete this.splitZone._domRef;
  2359. delete Ext.dd.DDM.ids["gridSplitters" + this.grid.getGridEl().id];
  2360. }
  2361. Ext.fly(this.innerHd).removeAllListeners();
  2362. Ext.removeNode(this.innerHd);
  2363. delete this.innerHd;
  2364. Ext.destroy(
  2365. this.el,
  2366. this.mainWrap,
  2367. this.mainHd,
  2368. this.scroller,
  2369. this.mainBody,
  2370. this.focusEl,
  2371. this.resizeMarker,
  2372. this.resizeProxy,
  2373. this.activeHdBtn,
  2374. this.dragZone,
  2375. this.splitZone,
  2376. this._flyweight
  2377. );
  2378. delete this.grid.container;
  2379. if(this.dragZone){
  2380. this.dragZone.destroy();
  2381. }
  2382. Ext.dd.DDM.currentTarget = null;
  2383. delete Ext.dd.DDM.locationCache[this.grid.getGridEl().id];
  2384. Ext.EventManager.removeResizeListener(this.onWindowResize, this);
  2385. },
  2386. // private
  2387. onDenyColumnHide : function(){
  2388. },
  2389. // private
  2390. render : function(){
  2391. if(this.autoFill){
  2392. var ct = this.grid.ownerCt;
  2393. if (ct && ct.getLayout()){
  2394. ct.on('afterlayout', function(){
  2395. this.fitColumns(true, true);
  2396. this.updateHeaders();
  2397. }, this, {single: true});
  2398. }else{
  2399. this.fitColumns(true, true);
  2400. }
  2401. }else if(this.forceFit){
  2402. this.fitColumns(true, false);
  2403. }else if(this.grid.autoExpandColumn){
  2404. this.autoExpand(true);
  2405. }
  2406. this.renderUI();
  2407. },
  2408. /* --------------------------------- Model Events and Handlers --------------------------------*/
  2409. // private
  2410. initData : function(ds, cm){
  2411. if(this.ds){
  2412. this.ds.un('load', this.onLoad, this);
  2413. this.ds.un('datachanged', this.onDataChange, this);
  2414. this.ds.un('add', this.onAdd, this);
  2415. this.ds.un('remove', this.onRemove, this);
  2416. this.ds.un('update', this.onUpdate, this);
  2417. this.ds.un('clear', this.onClear, this);
  2418. if(this.ds !== ds && this.ds.autoDestroy){
  2419. this.ds.destroy();
  2420. }
  2421. }
  2422. if(ds){
  2423. ds.on({
  2424. scope: this,
  2425. load: this.onLoad,
  2426. datachanged: this.onDataChange,
  2427. add: this.onAdd,
  2428. remove: this.onRemove,
  2429. update: this.onUpdate,
  2430. clear: this.onClear
  2431. });
  2432. }
  2433. this.ds = ds;
  2434. if(this.cm){
  2435. this.cm.un('configchange', this.onColConfigChange, this);
  2436. this.cm.un('widthchange', this.onColWidthChange, this);
  2437. this.cm.un('headerchange', this.onHeaderChange, this);
  2438. this.cm.un('hiddenchange', this.onHiddenChange, this);
  2439. this.cm.un('columnmoved', this.onColumnMove, this);
  2440. }
  2441. if(cm){
  2442. delete this.lastViewWidth;
  2443. cm.on({
  2444. scope: this,
  2445. configchange: this.onColConfigChange,
  2446. widthchange: this.onColWidthChange,
  2447. headerchange: this.onHeaderChange,
  2448. hiddenchange: this.onHiddenChange,
  2449. columnmoved: this.onColumnMove
  2450. });
  2451. }
  2452. this.cm = cm;
  2453. },
  2454. // private
  2455. onDataChange : function(){
  2456. this.refresh();
  2457. this.updateHeaderSortState();
  2458. this.syncFocusEl(0);
  2459. },
  2460. // private
  2461. onClear : function(){
  2462. this.refresh();
  2463. this.syncFocusEl(0);
  2464. },
  2465. // private
  2466. onUpdate : function(ds, record){
  2467. this.refreshRow(record);
  2468. },
  2469. // private
  2470. onAdd : function(ds, records, index){
  2471. this.insertRows(ds, index, index + (records.length-1));
  2472. },
  2473. // private
  2474. onRemove : function(ds, record, index, isUpdate){
  2475. if(isUpdate !== true){
  2476. this.fireEvent('beforerowremoved', this, index, record);
  2477. }
  2478. this.removeRow(index);
  2479. if(isUpdate !== true){
  2480. this.processRows(index);
  2481. this.applyEmptyText();
  2482. this.fireEvent('rowremoved', this, index, record);
  2483. }
  2484. },
  2485. // private
  2486. onLoad : function(){
  2487. if (Ext.isGecko){
  2488. if (!this.scrollToTopTask) {
  2489. this.scrollToTopTask = new Ext.util.DelayedTask(this.scrollToTop, this);
  2490. }
  2491. this.scrollToTopTask.delay(1);
  2492. }else{
  2493. this.scrollToTop();
  2494. }
  2495. },
  2496. // private
  2497. onColWidthChange : function(cm, col, width){
  2498. this.updateColumnWidth(col, width);
  2499. },
  2500. // private
  2501. onHeaderChange : function(cm, col, text){
  2502. this.updateHeaders();
  2503. },
  2504. // private
  2505. onHiddenChange : function(cm, col, hidden){
  2506. this.updateColumnHidden(col, hidden);
  2507. },
  2508. // private
  2509. onColumnMove : function(cm, oldIndex, newIndex){
  2510. this.indexMap = null;
  2511. var s = this.getScrollState();
  2512. this.refresh(true);
  2513. this.restoreScroll(s);
  2514. this.afterMove(newIndex);
  2515. this.grid.fireEvent('columnmove', oldIndex, newIndex);
  2516. },
  2517. // private
  2518. onColConfigChange : function(){
  2519. delete this.lastViewWidth;
  2520. this.indexMap = null;
  2521. this.refresh(true);
  2522. },
  2523. /* -------------------- UI Events and Handlers ------------------------------ */
  2524. // private
  2525. initUI : function(grid){
  2526. grid.on('headerclick', this.onHeaderClick, this);
  2527. },
  2528. // private
  2529. initEvents : function(){
  2530. },
  2531. // private
  2532. onHeaderClick : function(g, index){
  2533. if(this.headersDisabled || !this.cm.isSortable(index)){
  2534. return;
  2535. }
  2536. g.stopEditing(true);
  2537. g.store.sort(this.cm.getDataIndex(index));
  2538. },
  2539. // private
  2540. onRowOver : function(e, t){
  2541. var row;
  2542. if((row = this.findRowIndex(t)) !== false){
  2543. this.addRowClass(row, 'x-grid3-row-over');
  2544. }
  2545. },
  2546. // private
  2547. onRowOut : function(e, t){
  2548. var row;
  2549. if((row = this.findRowIndex(t)) !== false && !e.within(this.getRow(row), true)){
  2550. this.removeRowClass(row, 'x-grid3-row-over');
  2551. }
  2552. },
  2553. // private
  2554. handleWheel : function(e){
  2555. e.stopPropagation();
  2556. },
  2557. // private
  2558. onRowSelect : function(row){
  2559. this.addRowClass(row, this.selectedRowClass);
  2560. },
  2561. // private
  2562. onRowDeselect : function(row){
  2563. this.removeRowClass(row, this.selectedRowClass);
  2564. },
  2565. // private
  2566. onCellSelect : function(row, col){
  2567. var cell = this.getCell(row, col);
  2568. if(cell){
  2569. this.fly(cell).addClass('x-grid3-cell-selected');
  2570. }
  2571. },
  2572. // private
  2573. onCellDeselect : function(row, col){
  2574. var cell = this.getCell(row, col);
  2575. if(cell){
  2576. this.fly(cell).removeClass('x-grid3-cell-selected');
  2577. }
  2578. },
  2579. // private
  2580. onColumnSplitterMoved : function(i, w){
  2581. this.userResized = true;
  2582. var cm = this.grid.colModel;
  2583. cm.setColumnWidth(i, w, true);
  2584. if(this.forceFit){
  2585. this.fitColumns(true, false, i);
  2586. this.updateAllColumnWidths();
  2587. }else{
  2588. this.updateColumnWidth(i, w);
  2589. this.syncHeaderScroll();
  2590. }
  2591. this.grid.fireEvent('columnresize', i, w);
  2592. },
  2593. // private
  2594. handleHdMenuClick : function(item){
  2595. var index = this.hdCtxIndex,
  2596. cm = this.cm,
  2597. ds = this.ds,
  2598. id = item.getItemId();
  2599. switch(id){
  2600. case 'asc':
  2601. ds.sort(cm.getDataIndex(index), 'ASC');
  2602. break;
  2603. case 'desc':
  2604. ds.sort(cm.getDataIndex(index), 'DESC');
  2605. break;
  2606. default:
  2607. index = cm.getIndexById(id.substr(4));
  2608. if(index != -1){
  2609. if(item.checked && cm.getColumnsBy(this.isHideableColumn, this).length <= 1){
  2610. this.onDenyColumnHide();
  2611. return false;
  2612. }
  2613. cm.setHidden(index, item.checked);
  2614. }
  2615. }
  2616. return true;
  2617. },
  2618. // private
  2619. isHideableColumn : function(c){
  2620. return !c.hidden;
  2621. },
  2622. // private
  2623. beforeColMenuShow : function(){
  2624. var cm = this.cm, colCount = cm.getColumnCount();
  2625. this.colMenu.removeAll();
  2626. for(var i = 0; i < colCount; i++){
  2627. if(cm.config[i].hideable !== false){
  2628. this.colMenu.add(new Ext.menu.CheckItem({
  2629. itemId: 'col-'+cm.getColumnId(i),
  2630. text: cm.getColumnHeader(i),
  2631. checked: !cm.isHidden(i),
  2632. hideOnClick:false,
  2633. disabled: cm.config[i].hideable === false
  2634. }));
  2635. }
  2636. }
  2637. },
  2638. // private
  2639. handleHdDown : function(e, t){
  2640. if(Ext.fly(t).hasClass('x-grid3-hd-btn')){
  2641. e.stopEvent();
  2642. var hd = this.findHeaderCell(t);
  2643. Ext.fly(hd).addClass('x-grid3-hd-menu-open');
  2644. var index = this.getCellIndex(hd);
  2645. this.hdCtxIndex = index;
  2646. var ms = this.hmenu.items, cm = this.cm;
  2647. ms.get('asc').setDisabled(!cm.isSortable(index));
  2648. ms.get('desc').setDisabled(!cm.isSortable(index));
  2649. this.hmenu.on('hide', function(){
  2650. Ext.fly(hd).removeClass('x-grid3-hd-menu-open');
  2651. }, this, {single:true});
  2652. this.hmenu.show(t, 'tl-bl?');
  2653. }
  2654. },
  2655. // private
  2656. handleHdOver : function(e, t){
  2657. var hd = this.findHeaderCell(t);
  2658. if(hd && !this.headersDisabled){
  2659. this.activeHdRef = t;
  2660. this.activeHdIndex = this.getCellIndex(hd);
  2661. var fly = this.fly(hd);
  2662. this.activeHdRegion = fly.getRegion();
  2663. if(!this.cm.isMenuDisabled(this.activeHdIndex)){
  2664. fly.addClass('x-grid3-hd-over');
  2665. this.activeHdBtn = fly.child('.x-grid3-hd-btn');
  2666. if(this.activeHdBtn){
  2667. this.activeHdBtn.dom.style.height = (hd.firstChild.offsetHeight-1)+'px';
  2668. }
  2669. }
  2670. }
  2671. },
  2672. // private
  2673. handleHdMove : function(e, t){
  2674. var hd = this.findHeaderCell(this.activeHdRef);
  2675. if(hd && !this.headersDisabled){
  2676. var hw = this.splitHandleWidth || 5,
  2677. r = this.activeHdRegion,
  2678. x = e.getPageX(),
  2679. ss = hd.style,
  2680. cur = '';
  2681. if(this.grid.enableColumnResize !== false){
  2682. if(x - r.left <= hw && this.cm.isResizable(this.activeHdIndex-1)){
  2683. cur = Ext.isAir ? 'move' : Ext.isWebKit ? 'e-resize' : 'col-resize'; // col-resize not always supported
  2684. }else if(r.right - x <= (!this.activeHdBtn ? hw : 2) && this.cm.isResizable(this.activeHdIndex)){
  2685. cur = Ext.isAir ? 'move' : Ext.isWebKit ? 'w-resize' : 'col-resize';
  2686. }
  2687. }
  2688. ss.cursor = cur;
  2689. }
  2690. },
  2691. // private
  2692. handleHdOut : function(e, t){
  2693. var hd = this.findHeaderCell(t);
  2694. if(hd && (!Ext.isIE || !e.within(hd, true))){
  2695. this.activeHdRef = null;
  2696. this.fly(hd).removeClass('x-grid3-hd-over');
  2697. hd.style.cursor = '';
  2698. }
  2699. },
  2700. // private
  2701. hasRows : function(){
  2702. var fc = this.mainBody.dom.firstChild;
  2703. return fc && fc.nodeType == 1 && fc.className != 'x-grid-empty';
  2704. },
  2705. // back compat
  2706. bind : function(d, c){
  2707. this.initData(d, c);
  2708. }
  2709. });
  2710. // private
  2711. // This is a support class used internally by the Grid components
  2712. Ext.grid.GridView.SplitDragZone = Ext.extend(Ext.dd.DDProxy, {
  2713. constructor: function(grid, hd){
  2714. this.grid = grid;
  2715. this.view = grid.getView();
  2716. this.marker = this.view.resizeMarker;
  2717. this.proxy = this.view.resizeProxy;
  2718. Ext.grid.GridView.SplitDragZone.superclass.constructor.call(this, hd,
  2719. 'gridSplitters' + this.grid.getGridEl().id, {
  2720. dragElId : Ext.id(this.proxy.dom), resizeFrame:false
  2721. });
  2722. this.scroll = false;
  2723. this.hw = this.view.splitHandleWidth || 5;
  2724. },
  2725. b4StartDrag : function(x, y){
  2726. this.dragHeadersDisabled = this.view.headersDisabled;
  2727. this.view.headersDisabled = true;
  2728. var h = this.view.mainWrap.getHeight();
  2729. this.marker.setHeight(h);
  2730. this.marker.show();
  2731. this.marker.alignTo(this.view.getHeaderCell(this.cellIndex), 'tl-tl', [-2, 0]);
  2732. this.proxy.setHeight(h);
  2733. var w = this.cm.getColumnWidth(this.cellIndex),
  2734. minw = Math.max(w-this.grid.minColumnWidth, 0);
  2735. this.resetConstraints();
  2736. this.setXConstraint(minw, 1000);
  2737. this.setYConstraint(0, 0);
  2738. this.minX = x - minw;
  2739. this.maxX = x + 1000;
  2740. this.startPos = x;
  2741. Ext.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
  2742. },
  2743. allowHeaderDrag : function(e){
  2744. return true;
  2745. },
  2746. handleMouseDown : function(e){
  2747. var t = this.view.findHeaderCell(e.getTarget());
  2748. if(t && this.allowHeaderDrag(e)){
  2749. var xy = this.view.fly(t).getXY(),
  2750. x = xy[0],
  2751. y = xy[1];
  2752. exy = e.getXY(), ex = exy[0],
  2753. w = t.offsetWidth, adjust = false;
  2754. if((ex - x) <= this.hw){
  2755. adjust = -1;
  2756. }else if((x+w) - ex <= this.hw){
  2757. adjust = 0;
  2758. }
  2759. if(adjust !== false){
  2760. this.cm = this.grid.colModel;
  2761. var ci = this.view.getCellIndex(t);
  2762. if(adjust == -1){
  2763. if (ci + adjust < 0) {
  2764. return;
  2765. }
  2766. while(this.cm.isHidden(ci+adjust)){
  2767. --adjust;
  2768. if(ci+adjust < 0){
  2769. return;
  2770. }
  2771. }
  2772. }
  2773. this.cellIndex = ci+adjust;
  2774. this.split = t.dom;
  2775. if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
  2776. Ext.grid.GridView.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
  2777. }
  2778. }else if(this.view.columnDrag){
  2779. this.view.columnDrag.callHandleMouseDown(e);
  2780. }
  2781. }
  2782. },
  2783. endDrag : function(e){
  2784. this.marker.hide();
  2785. var v = this.view,
  2786. endX = Math.max(this.minX, e.getPageX()),
  2787. diff = endX - this.startPos,
  2788. disabled = this.dragHeadersDisabled;
  2789. v.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
  2790. setTimeout(function(){
  2791. v.headersDisabled = disabled;
  2792. }, 50);
  2793. },
  2794. autoOffset : function(){
  2795. this.setDelta(0,0);
  2796. }
  2797. });
  2798. // private
  2799. // This is a support class used internally by the Grid components
  2800. Ext.grid.HeaderDragZone = Ext.extend(Ext.dd.DragZone, {
  2801. maxDragWidth: 120,
  2802. constructor : function(grid, hd, hd2){
  2803. this.grid = grid;
  2804. this.view = grid.getView();
  2805. this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
  2806. Ext.grid.HeaderDragZone.superclass.constructor.call(this, hd);
  2807. if(hd2){
  2808. this.setHandleElId(Ext.id(hd));
  2809. this.setOuterHandleElId(Ext.id(hd2));
  2810. }
  2811. this.scroll = false;
  2812. },
  2813. getDragData : function(e){
  2814. var t = Ext.lib.Event.getTarget(e),
  2815. h = this.view.findHeaderCell(t);
  2816. if(h){
  2817. return {ddel: h.firstChild, header:h};
  2818. }
  2819. return false;
  2820. },
  2821. onInitDrag : function(e){
  2822. // keep the value here so we can restore it;
  2823. this.dragHeadersDisabled = this.view.headersDisabled;
  2824. this.view.headersDisabled = true;
  2825. var clone = this.dragData.ddel.cloneNode(true);
  2826. clone.id = Ext.id();
  2827. clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
  2828. this.proxy.update(clone);
  2829. return true;
  2830. },
  2831. afterValidDrop : function(){
  2832. this.completeDrop();
  2833. },
  2834. afterInvalidDrop : function(){
  2835. this.completeDrop();
  2836. },
  2837. completeDrop: function(){
  2838. var v = this.view,
  2839. disabled = this.dragHeadersDisabled;
  2840. setTimeout(function(){
  2841. v.headersDisabled = disabled;
  2842. }, 50);
  2843. }
  2844. });
  2845. // private
  2846. // This is a support class used internally by the Grid components
  2847. Ext.grid.HeaderDropZone = Ext.extend(Ext.dd.DropZone, {
  2848. proxyOffsets : [-4, -9],
  2849. fly: Ext.Element.fly,
  2850. constructor : function(grid, hd, hd2){
  2851. this.grid = grid;
  2852. this.view = grid.getView();
  2853. // split the proxies so they don't interfere with mouse events
  2854. this.proxyTop = Ext.DomHelper.append(document.body, {
  2855. cls:"col-move-top", html:"&#160;"
  2856. }, true);
  2857. this.proxyBottom = Ext.DomHelper.append(document.body, {
  2858. cls:"col-move-bottom", html:"&#160;"
  2859. }, true);
  2860. this.proxyTop.hide = this.proxyBottom.hide = function(){
  2861. this.setLeftTop(-100,-100);
  2862. this.setStyle("visibility", "hidden");
  2863. };
  2864. this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
  2865. // temporarily disabled
  2866. //Ext.dd.ScrollManager.register(this.view.scroller.dom);
  2867. Ext.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
  2868. },
  2869. getTargetFromEvent : function(e){
  2870. var t = Ext.lib.Event.getTarget(e),
  2871. cindex = this.view.findCellIndex(t);
  2872. if(cindex !== false){
  2873. return this.view.getHeaderCell(cindex);
  2874. }
  2875. },
  2876. nextVisible : function(h){
  2877. var v = this.view, cm = this.grid.colModel;
  2878. h = h.nextSibling;
  2879. while(h){
  2880. if(!cm.isHidden(v.getCellIndex(h))){
  2881. return h;
  2882. }
  2883. h = h.nextSibling;
  2884. }
  2885. return null;
  2886. },
  2887. prevVisible : function(h){
  2888. var v = this.view, cm = this.grid.colModel;
  2889. h = h.prevSibling;
  2890. while(h){
  2891. if(!cm.isHidden(v.getCellIndex(h))){
  2892. return h;
  2893. }
  2894. h = h.prevSibling;
  2895. }
  2896. return null;
  2897. },
  2898. positionIndicator : function(h, n, e){
  2899. var x = Ext.lib.Event.getPageX(e),
  2900. r = Ext.lib.Dom.getRegion(n.firstChild),
  2901. px,
  2902. pt,
  2903. py = r.top + this.proxyOffsets[1];
  2904. if((r.right - x) <= (r.right-r.left)/2){
  2905. px = r.right+this.view.borderWidth;
  2906. pt = "after";
  2907. }else{
  2908. px = r.left;
  2909. pt = "before";
  2910. }
  2911. if(this.grid.colModel.isFixed(this.view.getCellIndex(n))){
  2912. return false;
  2913. }
  2914. px += this.proxyOffsets[0];
  2915. this.proxyTop.setLeftTop(px, py);
  2916. this.proxyTop.show();
  2917. if(!this.bottomOffset){
  2918. this.bottomOffset = this.view.mainHd.getHeight();
  2919. }
  2920. this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
  2921. this.proxyBottom.show();
  2922. return pt;
  2923. },
  2924. onNodeEnter : function(n, dd, e, data){
  2925. if(data.header != n){
  2926. this.positionIndicator(data.header, n, e);
  2927. }
  2928. },
  2929. onNodeOver : function(n, dd, e, data){
  2930. var result = false;
  2931. if(data.header != n){
  2932. result = this.positionIndicator(data.header, n, e);
  2933. }
  2934. if(!result){
  2935. this.proxyTop.hide();
  2936. this.proxyBottom.hide();
  2937. }
  2938. return result ? this.dropAllowed : this.dropNotAllowed;
  2939. },
  2940. onNodeOut : function(n, dd, e, data){
  2941. this.proxyTop.hide();
  2942. this.proxyBottom.hide();
  2943. },
  2944. onNodeDrop : function(n, dd, e, data){
  2945. var h = data.header;
  2946. if(h != n){
  2947. var cm = this.grid.colModel,
  2948. x = Ext.lib.Event.getPageX(e),
  2949. r = Ext.lib.Dom.getRegion(n.firstChild),
  2950. pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before",
  2951. oldIndex = this.view.getCellIndex(h),
  2952. newIndex = this.view.getCellIndex(n);
  2953. if(pt == "after"){
  2954. newIndex++;
  2955. }
  2956. if(oldIndex < newIndex){
  2957. newIndex--;
  2958. }
  2959. cm.moveColumn(oldIndex, newIndex);
  2960. return true;
  2961. }
  2962. return false;
  2963. }
  2964. });
  2965. Ext.grid.GridView.ColumnDragZone = Ext.extend(Ext.grid.HeaderDragZone, {
  2966. constructor : function(grid, hd){
  2967. Ext.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
  2968. this.proxy.el.addClass('x-grid3-col-dd');
  2969. },
  2970. handleMouseDown : function(e){
  2971. },
  2972. callHandleMouseDown : function(e){
  2973. Ext.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
  2974. }
  2975. });// private
  2976. // This is a support class used internally by the Grid components
  2977. Ext.grid.SplitDragZone = Ext.extend(Ext.dd.DDProxy, {
  2978. fly: Ext.Element.fly,
  2979. constructor : function(grid, hd, hd2){
  2980. this.grid = grid;
  2981. this.view = grid.getView();
  2982. this.proxy = this.view.resizeProxy;
  2983. Ext.grid.SplitDragZone.superclass.constructor.call(this, hd,
  2984. "gridSplitters" + this.grid.getGridEl().id, {
  2985. dragElId : Ext.id(this.proxy.dom), resizeFrame:false
  2986. });
  2987. this.setHandleElId(Ext.id(hd));
  2988. this.setOuterHandleElId(Ext.id(hd2));
  2989. this.scroll = false;
  2990. },
  2991. b4StartDrag : function(x, y){
  2992. this.view.headersDisabled = true;
  2993. this.proxy.setHeight(this.view.mainWrap.getHeight());
  2994. var w = this.cm.getColumnWidth(this.cellIndex);
  2995. var minw = Math.max(w-this.grid.minColumnWidth, 0);
  2996. this.resetConstraints();
  2997. this.setXConstraint(minw, 1000);
  2998. this.setYConstraint(0, 0);
  2999. this.minX = x - minw;
  3000. this.maxX = x + 1000;
  3001. this.startPos = x;
  3002. Ext.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
  3003. },
  3004. handleMouseDown : function(e){
  3005. var ev = Ext.EventObject.setEvent(e);
  3006. var t = this.fly(ev.getTarget());
  3007. if(t.hasClass("x-grid-split")){
  3008. this.cellIndex = this.view.getCellIndex(t.dom);
  3009. this.split = t.dom;
  3010. this.cm = this.grid.colModel;
  3011. if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
  3012. Ext.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
  3013. }
  3014. }
  3015. },
  3016. endDrag : function(e){
  3017. this.view.headersDisabled = false;
  3018. var endX = Math.max(this.minX, Ext.lib.Event.getPageX(e));
  3019. var diff = endX - this.startPos;
  3020. this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
  3021. },
  3022. autoOffset : function(){
  3023. this.setDelta(0,0);
  3024. }
  3025. });/**
  3026. * @class Ext.grid.GridDragZone
  3027. * @extends Ext.dd.DragZone
  3028. * <p>A customized implementation of a {@link Ext.dd.DragZone DragZone} which provides default implementations of two of the
  3029. * template methods of DragZone to enable dragging of the selected rows of a GridPanel.</p>
  3030. * <p>A cooperating {@link Ext.dd.DropZone DropZone} must be created who's template method implementations of
  3031. * {@link Ext.dd.DropZone#onNodeEnter onNodeEnter}, {@link Ext.dd.DropZone#onNodeOver onNodeOver},
  3032. * {@link Ext.dd.DropZone#onNodeOut onNodeOut} and {@link Ext.dd.DropZone#onNodeDrop onNodeDrop}</p> are able
  3033. * to process the {@link #getDragData data} which is provided.
  3034. */
  3035. Ext.grid.GridDragZone = function(grid, config){
  3036. this.view = grid.getView();
  3037. Ext.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
  3038. this.scroll = false;
  3039. this.grid = grid;
  3040. this.ddel = document.createElement('div');
  3041. this.ddel.className = 'x-grid-dd-wrap';
  3042. };
  3043. Ext.extend(Ext.grid.GridDragZone, Ext.dd.DragZone, {
  3044. ddGroup : "GridDD",
  3045. /**
  3046. * <p>The provided implementation of the getDragData method which collects the data to be dragged from the GridPanel on mousedown.</p>
  3047. * <p>This data is available for processing in the {@link Ext.dd.DropZone#onNodeEnter onNodeEnter}, {@link Ext.dd.DropZone#onNodeOver onNodeOver},
  3048. * {@link Ext.dd.DropZone#onNodeOut onNodeOut} and {@link Ext.dd.DropZone#onNodeDrop onNodeDrop} methods of a cooperating {@link Ext.dd.DropZone DropZone}.</p>
  3049. * <p>The data object contains the following properties:<ul>
  3050. * <li><b>grid</b> : Ext.Grid.GridPanel<div class="sub-desc">The GridPanel from which the data is being dragged.</div></li>
  3051. * <li><b>ddel</b> : htmlElement<div class="sub-desc">An htmlElement which provides the "picture" of the data being dragged.</div></li>
  3052. * <li><b>rowIndex</b> : Number<div class="sub-desc">The index of the row which receieved the mousedown gesture which triggered the drag.</div></li>
  3053. * <li><b>selections</b> : Array<div class="sub-desc">An Array of the selected Records which are being dragged from the GridPanel.</div></li>
  3054. * </ul></p>
  3055. */
  3056. getDragData : function(e){
  3057. var t = Ext.lib.Event.getTarget(e);
  3058. var rowIndex = this.view.findRowIndex(t);
  3059. if(rowIndex !== false){
  3060. var sm = this.grid.selModel;
  3061. if(!sm.isSelected(rowIndex) || e.hasModifier()){
  3062. sm.handleMouseDown(this.grid, rowIndex, e);
  3063. }
  3064. return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
  3065. }
  3066. return false;
  3067. },
  3068. /**
  3069. * <p>The provided implementation of the onInitDrag method. Sets the <tt>innerHTML</tt> of the drag proxy which provides the "picture"
  3070. * of the data being dragged.</p>
  3071. * <p>The <tt>innerHTML</tt> data is found by calling the owning GridPanel's {@link Ext.grid.GridPanel#getDragDropText getDragDropText}.</p>
  3072. */
  3073. onInitDrag : function(e){
  3074. var data = this.dragData;
  3075. this.ddel.innerHTML = this.grid.getDragDropText();
  3076. this.proxy.update(this.ddel);
  3077. // fire start drag?
  3078. },
  3079. /**
  3080. * An empty immplementation. Implement this to provide behaviour after a repair of an invalid drop. An implementation might highlight
  3081. * the selected rows to show that they have not been dragged.
  3082. */
  3083. afterRepair : function(){
  3084. this.dragging = false;
  3085. },
  3086. /**
  3087. * <p>An empty implementation. Implement this to provide coordinates for the drag proxy to slide back to after an invalid drop.</p>
  3088. * <p>Called before a repair of an invalid drop to get the XY to animate to.</p>
  3089. * @param {EventObject} e The mouse up event
  3090. * @return {Array} The xy location (e.g. [100, 200])
  3091. */
  3092. getRepairXY : function(e, data){
  3093. return false;
  3094. },
  3095. onEndDrag : function(data, e){
  3096. // fire end drag?
  3097. },
  3098. onValidDrop : function(dd, e, id){
  3099. // fire drag drop?
  3100. this.hideProxy();
  3101. },
  3102. beforeInvalidDrop : function(e, id){
  3103. }
  3104. });
  3105. /**
  3106. * @class Ext.grid.ColumnModel
  3107. * @extends Ext.util.Observable
  3108. * <p>After the data has been read into the client side cache (<b>{@link Ext.data.Store Store}</b>),
  3109. * the ColumnModel is used to configure how and what parts of that data will be displayed in the
  3110. * vertical slices (columns) of the grid. The Ext.grid.ColumnModel Class is the default implementation
  3111. * of a ColumnModel used by implentations of {@link Ext.grid.GridPanel GridPanel}.</p>
  3112. * <p>Data is mapped into the store's records and then indexed into the ColumnModel using the
  3113. * <tt>{@link Ext.grid.Column#dataIndex dataIndex}</tt>:</p>
  3114. * <pre><code>
  3115. {data source} == mapping ==> {data store} == <b><tt>{@link Ext.grid.Column#dataIndex dataIndex}</tt></b> ==> {ColumnModel}
  3116. * </code></pre>
  3117. * <p>Each {@link Ext.grid.Column Column} in the grid's ColumnModel is configured with a
  3118. * <tt>{@link Ext.grid.Column#dataIndex dataIndex}</tt> to specify how the data within
  3119. * each record in the store is indexed into the ColumnModel.</p>
  3120. * <p>There are two ways to initialize the ColumnModel class:</p>
  3121. * <p><u>Initialization Method 1: an Array</u></p>
  3122. <pre><code>
  3123. var colModel = new Ext.grid.ColumnModel([
  3124. { header: "Ticker", width: 60, sortable: true},
  3125. { header: "Company Name", width: 150, sortable: true, id: 'company'},
  3126. { header: "Market Cap.", width: 100, sortable: true},
  3127. { header: "$ Sales", width: 100, sortable: true, renderer: money},
  3128. { header: "Employees", width: 100, sortable: true, resizable: false}
  3129. ]);
  3130. </code></pre>
  3131. * <p>The ColumnModel may be initialized with an Array of {@link Ext.grid.Column} column configuration
  3132. * objects to define the initial layout / display of the columns in the Grid. The order of each
  3133. * {@link Ext.grid.Column} column configuration object within the specified Array defines the initial
  3134. * order of the column display. A Column's display may be initially hidden using the
  3135. * <tt>{@link Ext.grid.Column#hidden hidden}</tt></b> config property (and then shown using the column
  3136. * header menu). Fields that are not included in the ColumnModel will not be displayable at all.</p>
  3137. * <p>How each column in the grid correlates (maps) to the {@link Ext.data.Record} field in the
  3138. * {@link Ext.data.Store Store} the column draws its data from is configured through the
  3139. * <b><tt>{@link Ext.grid.Column#dataIndex dataIndex}</tt></b>. If the
  3140. * <b><tt>{@link Ext.grid.Column#dataIndex dataIndex}</tt></b> is not explicitly defined (as shown in the
  3141. * example above) it will use the column configuration's index in the Array as the index.</p>
  3142. * <p>See <b><tt>{@link Ext.grid.Column}</tt></b> for additional configuration options for each column.</p>
  3143. * <p><u>Initialization Method 2: an Object</u></p>
  3144. * <p>In order to use configuration options from <tt>Ext.grid.ColumnModel</tt>, an Object may be used to
  3145. * initialize the ColumnModel. The column configuration Array will be specified in the <tt><b>{@link #columns}</b></tt>
  3146. * config property. The <tt><b>{@link #defaults}</b></tt> config property can be used to apply defaults
  3147. * for all columns, e.g.:</p><pre><code>
  3148. var colModel = new Ext.grid.ColumnModel({
  3149. columns: [
  3150. { header: "Ticker", width: 60, menuDisabled: false},
  3151. { header: "Company Name", width: 150, id: 'company'},
  3152. { header: "Market Cap."},
  3153. { header: "$ Sales", renderer: money},
  3154. { header: "Employees", resizable: false}
  3155. ],
  3156. defaults: {
  3157. sortable: true,
  3158. menuDisabled: true,
  3159. width: 100
  3160. },
  3161. listeners: {
  3162. {@link #hiddenchange}: function(cm, colIndex, hidden) {
  3163. saveConfig(colIndex, hidden);
  3164. }
  3165. }
  3166. });
  3167. </code></pre>
  3168. * <p>In both examples above, the ability to apply a CSS class to all cells in a column (including the
  3169. * header) is demonstrated through the use of the <b><tt>{@link Ext.grid.Column#id id}</tt></b> config
  3170. * option. This column could be styled by including the following css:</p><pre><code>
  3171. //add this css *after* the core css is loaded
  3172. .x-grid3-td-company {
  3173. color: red; // entire column will have red font
  3174. }
  3175. // modify the header row only, adding an icon to the column header
  3176. .x-grid3-hd-company {
  3177. background: transparent
  3178. url(../../resources/images/icons/silk/building.png)
  3179. no-repeat 3px 3px ! important;
  3180. padding-left:20px;
  3181. }
  3182. </code></pre>
  3183. * Note that the "Company Name" column could be specified as the
  3184. * <b><tt>{@link Ext.grid.GridPanel}.{@link Ext.grid.GridPanel#autoExpandColumn autoExpandColumn}</tt></b>.
  3185. * @constructor
  3186. * @param {Mixed} config Specify either an Array of {@link Ext.grid.Column} configuration objects or specify
  3187. * a configuration Object (see introductory section discussion utilizing Initialization Method 2 above).
  3188. */
  3189. Ext.grid.ColumnModel = Ext.extend(Ext.util.Observable, {
  3190. /**
  3191. * @cfg {Number} defaultWidth (optional) The width of columns which have no <tt>{@link #width}</tt>
  3192. * specified (defaults to <tt>100</tt>). This property shall preferably be configured through the
  3193. * <tt><b>{@link #defaults}</b></tt> config property.
  3194. */
  3195. defaultWidth: 100,
  3196. /**
  3197. * @cfg {Boolean} defaultSortable (optional) Default sortable of columns which have no
  3198. * sortable specified (defaults to <tt>false</tt>). This property shall preferably be configured
  3199. * through the <tt><b>{@link #defaults}</b></tt> config property.
  3200. */
  3201. defaultSortable: false,
  3202. /**
  3203. * @cfg {Array} columns An Array of object literals. The config options defined by
  3204. * <b>{@link Ext.grid.Column}</b> are the options which may appear in the object literal for each
  3205. * individual column definition.
  3206. */
  3207. /**
  3208. * @cfg {Object} defaults Object literal which will be used to apply {@link Ext.grid.Column}
  3209. * configuration options to all <tt><b>{@link #columns}</b></tt>. Configuration options specified with
  3210. * individual {@link Ext.grid.Column column} configs will supersede these <tt><b>{@link #defaults}</b></tt>.
  3211. */
  3212. constructor : function(config){
  3213. /**
  3214. * An Array of {@link Ext.grid.Column Column definition} objects representing the configuration
  3215. * of this ColumnModel. See {@link Ext.grid.Column} for the configuration properties that may
  3216. * be specified.
  3217. * @property config
  3218. * @type Array
  3219. */
  3220. if(config.columns){
  3221. Ext.apply(this, config);
  3222. this.setConfig(config.columns, true);
  3223. }else{
  3224. this.setConfig(config, true);
  3225. }
  3226. this.addEvents(
  3227. /**
  3228. * @event widthchange
  3229. * Fires when the width of a column is programmaticially changed using
  3230. * <code>{@link #setColumnWidth}</code>.
  3231. * Note internal resizing suppresses the event from firing. See also
  3232. * {@link Ext.grid.GridPanel}.<code>{@link #columnresize}</code>.
  3233. * @param {ColumnModel} this
  3234. * @param {Number} columnIndex The column index
  3235. * @param {Number} newWidth The new width
  3236. */
  3237. "widthchange",
  3238. /**
  3239. * @event headerchange
  3240. * Fires when the text of a header changes.
  3241. * @param {ColumnModel} this
  3242. * @param {Number} columnIndex The column index
  3243. * @param {String} newText The new header text
  3244. */
  3245. "headerchange",
  3246. /**
  3247. * @event hiddenchange
  3248. * Fires when a column is hidden or "unhidden".
  3249. * @param {ColumnModel} this
  3250. * @param {Number} columnIndex The column index
  3251. * @param {Boolean} hidden true if hidden, false otherwise
  3252. */
  3253. "hiddenchange",
  3254. /**
  3255. * @event columnmoved
  3256. * Fires when a column is moved.
  3257. * @param {ColumnModel} this
  3258. * @param {Number} oldIndex
  3259. * @param {Number} newIndex
  3260. */
  3261. "columnmoved",
  3262. /**
  3263. * @event configchange
  3264. * Fires when the configuration is changed
  3265. * @param {ColumnModel} this
  3266. */
  3267. "configchange"
  3268. );
  3269. Ext.grid.ColumnModel.superclass.constructor.call(this);
  3270. },
  3271. /**
  3272. * Returns the id of the column at the specified index.
  3273. * @param {Number} index The column index
  3274. * @return {String} the id
  3275. */
  3276. getColumnId : function(index){
  3277. return this.config[index].id;
  3278. },
  3279. getColumnAt : function(index){
  3280. return this.config[index];
  3281. },
  3282. /**
  3283. * <p>Reconfigures this column model according to the passed Array of column definition objects.
  3284. * For a description of the individual properties of a column definition object, see the
  3285. * <a href="#Ext.grid.ColumnModel-configs">Config Options</a>.</p>
  3286. * <p>Causes the {@link #configchange} event to be fired. A {@link Ext.grid.GridPanel GridPanel}
  3287. * using this ColumnModel will listen for this event and refresh its UI automatically.</p>
  3288. * @param {Array} config Array of Column definition objects.
  3289. * @param {Boolean} initial Specify <tt>true</tt> to bypass cleanup which deletes the <tt>totalWidth</tt>
  3290. * and destroys existing editors.
  3291. */
  3292. setConfig : function(config, initial){
  3293. var i, c, len;
  3294. if(!initial){ // cleanup
  3295. delete this.totalWidth;
  3296. for(i = 0, len = this.config.length; i < len; i++){
  3297. c = this.config[i];
  3298. if(c.setEditor){
  3299. //check here, in case we have a special column like a CheckboxSelectionModel
  3300. c.setEditor(null);
  3301. }
  3302. }
  3303. }
  3304. // backward compatibility
  3305. this.defaults = Ext.apply({
  3306. width: this.defaultWidth,
  3307. sortable: this.defaultSortable
  3308. }, this.defaults);
  3309. this.config = config;
  3310. this.lookup = {};
  3311. for(i = 0, len = config.length; i < len; i++){
  3312. c = Ext.applyIf(config[i], this.defaults);
  3313. // if no id, create one using column's ordinal position
  3314. if(Ext.isEmpty(c.id)){
  3315. c.id = i;
  3316. }
  3317. if(!c.isColumn){
  3318. var Cls = Ext.grid.Column.types[c.xtype || 'gridcolumn'];
  3319. c = new Cls(c);
  3320. config[i] = c;
  3321. }
  3322. this.lookup[c.id] = c;
  3323. }
  3324. if(!initial){
  3325. this.fireEvent('configchange', this);
  3326. }
  3327. },
  3328. /**
  3329. * Returns the column for a specified id.
  3330. * @param {String} id The column id
  3331. * @return {Object} the column
  3332. */
  3333. getColumnById : function(id){
  3334. return this.lookup[id];
  3335. },
  3336. /**
  3337. * Returns the index for a specified column id.
  3338. * @param {String} id The column id
  3339. * @return {Number} the index, or -1 if not found
  3340. */
  3341. getIndexById : function(id){
  3342. for(var i = 0, len = this.config.length; i < len; i++){
  3343. if(this.config[i].id == id){
  3344. return i;
  3345. }
  3346. }
  3347. return -1;
  3348. },
  3349. /**
  3350. * Moves a column from one position to another.
  3351. * @param {Number} oldIndex The index of the column to move.
  3352. * @param {Number} newIndex The position at which to reinsert the coolumn.
  3353. */
  3354. moveColumn : function(oldIndex, newIndex){
  3355. var c = this.config[oldIndex];
  3356. this.config.splice(oldIndex, 1);
  3357. this.config.splice(newIndex, 0, c);
  3358. this.dataMap = null;
  3359. this.fireEvent("columnmoved", this, oldIndex, newIndex);
  3360. },
  3361. /**
  3362. * Returns the number of columns.
  3363. * @param {Boolean} visibleOnly Optional. Pass as true to only include visible columns.
  3364. * @return {Number}
  3365. */
  3366. getColumnCount : function(visibleOnly){
  3367. if(visibleOnly === true){
  3368. var c = 0;
  3369. for(var i = 0, len = this.config.length; i < len; i++){
  3370. if(!this.isHidden(i)){
  3371. c++;
  3372. }
  3373. }
  3374. return c;
  3375. }
  3376. return this.config.length;
  3377. },
  3378. /**
  3379. * Returns the column configs that return true by the passed function that is called
  3380. * with (columnConfig, index)
  3381. <pre><code>
  3382. // returns an array of column config objects for all hidden columns
  3383. var columns = grid.getColumnModel().getColumnsBy(function(c){
  3384. return c.hidden;
  3385. });
  3386. </code></pre>
  3387. * @param {Function} fn A function which, when passed a {@link Ext.grid.Column Column} object, must
  3388. * return <code>true</code> if the column is to be included in the returned Array.
  3389. * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function
  3390. * is executed. Defaults to this ColumnModel.
  3391. * @return {Array} result
  3392. */
  3393. getColumnsBy : function(fn, scope){
  3394. var r = [];
  3395. for(var i = 0, len = this.config.length; i < len; i++){
  3396. var c = this.config[i];
  3397. if(fn.call(scope||this, c, i) === true){
  3398. r[r.length] = c;
  3399. }
  3400. }
  3401. return r;
  3402. },
  3403. /**
  3404. * Returns true if the specified column is sortable.
  3405. * @param {Number} col The column index
  3406. * @return {Boolean}
  3407. */
  3408. isSortable : function(col){
  3409. return !!this.config[col].sortable;
  3410. },
  3411. /**
  3412. * Returns true if the specified column menu is disabled.
  3413. * @param {Number} col The column index
  3414. * @return {Boolean}
  3415. */
  3416. isMenuDisabled : function(col){
  3417. return !!this.config[col].menuDisabled;
  3418. },
  3419. /**
  3420. * Returns the rendering (formatting) function defined for the column.
  3421. * @param {Number} col The column index.
  3422. * @return {Function} The function used to render the cell. See {@link #setRenderer}.
  3423. */
  3424. getRenderer : function(col){
  3425. if(!this.config[col].renderer){
  3426. return Ext.grid.ColumnModel.defaultRenderer;
  3427. }
  3428. return this.config[col].renderer;
  3429. },
  3430. getRendererScope : function(col){
  3431. return this.config[col].scope;
  3432. },
  3433. /**
  3434. * Sets the rendering (formatting) function for a column. See {@link Ext.util.Format} for some
  3435. * default formatting functions.
  3436. * @param {Number} col The column index
  3437. * @param {Function} fn The function to use to process the cell's raw data
  3438. * to return HTML markup for the grid view. The render function is called with
  3439. * the following parameters:<ul>
  3440. * <li><b>value</b> : Object<p class="sub-desc">The data value for the cell.</p></li>
  3441. * <li><b>metadata</b> : Object<p class="sub-desc">An object in which you may set the following attributes:<ul>
  3442. * <li><b>css</b> : String<p class="sub-desc">A CSS class name to add to the cell's TD element.</p></li>
  3443. * <li><b>attr</b> : String<p class="sub-desc">An HTML attribute definition string to apply to the data container element <i>within</i> the table cell
  3444. * (e.g. 'style="color:red;"').</p></li></ul></p></li>
  3445. * <li><b>record</b> : Ext.data.record<p class="sub-desc">The {@link Ext.data.Record} from which the data was extracted.</p></li>
  3446. * <li><b>rowIndex</b> : Number<p class="sub-desc">Row index</p></li>
  3447. * <li><b>colIndex</b> : Number<p class="sub-desc">Column index</p></li>
  3448. * <li><b>store</b> : Ext.data.Store<p class="sub-desc">The {@link Ext.data.Store} object from which the Record was extracted.</p></li></ul>
  3449. */
  3450. setRenderer : function(col, fn){
  3451. this.config[col].renderer = fn;
  3452. },
  3453. /**
  3454. * Returns the width for the specified column.
  3455. * @param {Number} col The column index
  3456. * @return {Number}
  3457. */
  3458. getColumnWidth : function(col){
  3459. return this.config[col].width;
  3460. },
  3461. /**
  3462. * Sets the width for a column.
  3463. * @param {Number} col The column index
  3464. * @param {Number} width The new width
  3465. * @param {Boolean} suppressEvent True to suppress firing the <code>{@link #widthchange}</code>
  3466. * event. Defaults to false.
  3467. */
  3468. setColumnWidth : function(col, width, suppressEvent){
  3469. this.config[col].width = width;
  3470. this.totalWidth = null;
  3471. if(!suppressEvent){
  3472. this.fireEvent("widthchange", this, col, width);
  3473. }
  3474. },
  3475. /**
  3476. * Returns the total width of all columns.
  3477. * @param {Boolean} includeHidden True to include hidden column widths
  3478. * @return {Number}
  3479. */
  3480. getTotalWidth : function(includeHidden){
  3481. if(!this.totalWidth){
  3482. this.totalWidth = 0;
  3483. for(var i = 0, len = this.config.length; i < len; i++){
  3484. if(includeHidden || !this.isHidden(i)){
  3485. this.totalWidth += this.getColumnWidth(i);
  3486. }
  3487. }
  3488. }
  3489. return this.totalWidth;
  3490. },
  3491. /**
  3492. * Returns the header for the specified column.
  3493. * @param {Number} col The column index
  3494. * @return {String}
  3495. */
  3496. getColumnHeader : function(col){
  3497. return this.config[col].header;
  3498. },
  3499. /**
  3500. * Sets the header for a column.
  3501. * @param {Number} col The column index
  3502. * @param {String} header The new header
  3503. */
  3504. setColumnHeader : function(col, header){
  3505. this.config[col].header = header;
  3506. this.fireEvent("headerchange", this, col, header);
  3507. },
  3508. /**
  3509. * Returns the tooltip for the specified column.
  3510. * @param {Number} col The column index
  3511. * @return {String}
  3512. */
  3513. getColumnTooltip : function(col){
  3514. return this.config[col].tooltip;
  3515. },
  3516. /**
  3517. * Sets the tooltip for a column.
  3518. * @param {Number} col The column index
  3519. * @param {String} tooltip The new tooltip
  3520. */
  3521. setColumnTooltip : function(col, tooltip){
  3522. this.config[col].tooltip = tooltip;
  3523. },
  3524. /**
  3525. * Returns the dataIndex for the specified column.
  3526. <pre><code>
  3527. // Get field name for the column
  3528. var fieldName = grid.getColumnModel().getDataIndex(columnIndex);
  3529. </code></pre>
  3530. * @param {Number} col The column index
  3531. * @return {String} The column's dataIndex
  3532. */
  3533. getDataIndex : function(col){
  3534. return this.config[col].dataIndex;
  3535. },
  3536. /**
  3537. * Sets the dataIndex for a column.
  3538. * @param {Number} col The column index
  3539. * @param {String} dataIndex The new dataIndex
  3540. */
  3541. setDataIndex : function(col, dataIndex){
  3542. this.config[col].dataIndex = dataIndex;
  3543. },
  3544. /**
  3545. * Finds the index of the first matching column for the given dataIndex.
  3546. * @param {String} col The dataIndex to find
  3547. * @return {Number} The column index, or -1 if no match was found
  3548. */
  3549. findColumnIndex : function(dataIndex){
  3550. var c = this.config;
  3551. for(var i = 0, len = c.length; i < len; i++){
  3552. if(c[i].dataIndex == dataIndex){
  3553. return i;
  3554. }
  3555. }
  3556. return -1;
  3557. },
  3558. /**
  3559. * Returns true if the cell is editable.
  3560. <pre><code>
  3561. var store = new Ext.data.Store({...});
  3562. var colModel = new Ext.grid.ColumnModel({
  3563. columns: [...],
  3564. isCellEditable: function(col, row) {
  3565. var record = store.getAt(row);
  3566. if (record.get('readonly')) { // replace with your condition
  3567. return false;
  3568. }
  3569. return Ext.grid.ColumnModel.prototype.isCellEditable.call(this, col, row);
  3570. }
  3571. });
  3572. var grid = new Ext.grid.GridPanel({
  3573. store: store,
  3574. colModel: colModel,
  3575. ...
  3576. });
  3577. </code></pre>
  3578. * @param {Number} colIndex The column index
  3579. * @param {Number} rowIndex The row index
  3580. * @return {Boolean}
  3581. */
  3582. isCellEditable : function(colIndex, rowIndex){
  3583. var c = this.config[colIndex],
  3584. ed = c.editable;
  3585. //force boolean
  3586. return !!(ed || (!Ext.isDefined(ed) && c.editor));
  3587. },
  3588. /**
  3589. * Returns the editor defined for the cell/column.
  3590. * @param {Number} colIndex The column index
  3591. * @param {Number} rowIndex The row index
  3592. * @return {Ext.Editor} The {@link Ext.Editor Editor} that was created to wrap
  3593. * the {@link Ext.form.Field Field} used to edit the cell.
  3594. */
  3595. getCellEditor : function(colIndex, rowIndex){
  3596. return this.config[colIndex].getCellEditor(rowIndex);
  3597. },
  3598. /**
  3599. * Sets if a column is editable.
  3600. * @param {Number} col The column index
  3601. * @param {Boolean} editable True if the column is editable
  3602. */
  3603. setEditable : function(col, editable){
  3604. this.config[col].editable = editable;
  3605. },
  3606. /**
  3607. * Returns <tt>true</tt> if the column is <code>{@link Ext.grid.Column#hidden hidden}</code>,
  3608. * <tt>false</tt> otherwise.
  3609. * @param {Number} colIndex The column index
  3610. * @return {Boolean}
  3611. */
  3612. isHidden : function(colIndex){
  3613. return !!this.config[colIndex].hidden; // ensure returns boolean
  3614. },
  3615. /**
  3616. * Returns <tt>true</tt> if the column is <code>{@link Ext.grid.Column#fixed fixed}</code>,
  3617. * <tt>false</tt> otherwise.
  3618. * @param {Number} colIndex The column index
  3619. * @return {Boolean}
  3620. */
  3621. isFixed : function(colIndex){
  3622. return !!this.config[colIndex].fixed;
  3623. },
  3624. /**
  3625. * Returns true if the column can be resized
  3626. * @return {Boolean}
  3627. */
  3628. isResizable : function(colIndex){
  3629. return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
  3630. },
  3631. /**
  3632. * Sets if a column is hidden.
  3633. <pre><code>
  3634. myGrid.getColumnModel().setHidden(0, true); // hide column 0 (0 = the first column).
  3635. </code></pre>
  3636. * @param {Number} colIndex The column index
  3637. * @param {Boolean} hidden True if the column is hidden
  3638. */
  3639. setHidden : function(colIndex, hidden){
  3640. var c = this.config[colIndex];
  3641. if(c.hidden !== hidden){
  3642. c.hidden = hidden;
  3643. this.totalWidth = null;
  3644. this.fireEvent("hiddenchange", this, colIndex, hidden);
  3645. }
  3646. },
  3647. /**
  3648. * Sets the editor for a column and destroys the prior editor.
  3649. * @param {Number} col The column index
  3650. * @param {Object} editor The editor object
  3651. */
  3652. setEditor : function(col, editor){
  3653. this.config[col].setEditor(editor);
  3654. },
  3655. /**
  3656. * Destroys this column model by purging any event listeners, and removing any editors.
  3657. */
  3658. destroy : function(){
  3659. var c;
  3660. for(var i = 0, len = this.config.length; i < len; i++){
  3661. c = this.config[i];
  3662. if(c.setEditor){
  3663. c.setEditor(null);
  3664. }
  3665. }
  3666. this.purgeListeners();
  3667. }
  3668. });
  3669. // private
  3670. Ext.grid.ColumnModel.defaultRenderer = function(value){
  3671. if(typeof value == "string" && value.length < 1){
  3672. return "&#160;";
  3673. }
  3674. return value;
  3675. };/**
  3676. * @class Ext.grid.AbstractSelectionModel
  3677. * @extends Ext.util.Observable
  3678. * Abstract base class for grid SelectionModels. It provides the interface that should be
  3679. * implemented by descendant classes. This class should not be directly instantiated.
  3680. * @constructor
  3681. */
  3682. Ext.grid.AbstractSelectionModel = Ext.extend(Ext.util.Observable, {
  3683. /**
  3684. * The GridPanel for which this SelectionModel is handling selection. Read-only.
  3685. * @type Object
  3686. * @property grid
  3687. */
  3688. constructor : function(){
  3689. this.locked = false;
  3690. Ext.grid.AbstractSelectionModel.superclass.constructor.call(this);
  3691. },
  3692. /** @ignore Called by the grid automatically. Do not call directly. */
  3693. init : function(grid){
  3694. this.grid = grid;
  3695. if(this.lockOnInit){
  3696. delete this.lockOnInit;
  3697. this.locked = false;
  3698. this.lock();
  3699. }
  3700. this.initEvents();
  3701. },
  3702. /**
  3703. * Locks the selections.
  3704. */
  3705. lock : function(){
  3706. if(!this.locked){
  3707. this.locked = true;
  3708. // If the grid has been set, then the view is already initialized.
  3709. var g = this.grid;
  3710. if(g){
  3711. g.getView().on({
  3712. scope: this,
  3713. beforerefresh: this.sortUnLock,
  3714. refresh: this.sortLock
  3715. });
  3716. }else{
  3717. this.lockOnInit = true;
  3718. }
  3719. }
  3720. },
  3721. // set the lock states before and after a view refresh
  3722. sortLock : function() {
  3723. this.locked = true;
  3724. },
  3725. // set the lock states before and after a view refresh
  3726. sortUnLock : function() {
  3727. this.locked = false;
  3728. },
  3729. /**
  3730. * Unlocks the selections.
  3731. */
  3732. unlock : function(){
  3733. if(this.locked){
  3734. this.locked = false;
  3735. var g = this.grid,
  3736. gv;
  3737. // If the grid has been set, then the view is already initialized.
  3738. if(g){
  3739. gv = g.getView();
  3740. gv.un('beforerefresh', this.sortUnLock, this);
  3741. gv.un('refresh', this.sortLock, this);
  3742. }else{
  3743. delete this.lockOnInit;
  3744. }
  3745. }
  3746. },
  3747. /**
  3748. * Returns true if the selections are locked.
  3749. * @return {Boolean}
  3750. */
  3751. isLocked : function(){
  3752. return this.locked;
  3753. },
  3754. destroy: function(){
  3755. this.unlock();
  3756. this.purgeListeners();
  3757. }
  3758. });/**
  3759. * @class Ext.grid.RowSelectionModel
  3760. * @extends Ext.grid.AbstractSelectionModel
  3761. * The default SelectionModel used by {@link Ext.grid.GridPanel}.
  3762. * It supports multiple selections and keyboard selection/navigation. The objects stored
  3763. * as selections and returned by {@link #getSelected}, and {@link #getSelections} are
  3764. * the {@link Ext.data.Record Record}s which provide the data for the selected rows.
  3765. * @constructor
  3766. * @param {Object} config
  3767. */
  3768. Ext.grid.RowSelectionModel = Ext.extend(Ext.grid.AbstractSelectionModel, {
  3769. /**
  3770. * @cfg {Boolean} singleSelect
  3771. * <tt>true</tt> to allow selection of only one row at a time (defaults to <tt>false</tt>
  3772. * allowing multiple selections)
  3773. */
  3774. singleSelect : false,
  3775. constructor : function(config){
  3776. Ext.apply(this, config);
  3777. this.selections = new Ext.util.MixedCollection(false, function(o){
  3778. return o.id;
  3779. });
  3780. this.last = false;
  3781. this.lastActive = false;
  3782. this.addEvents(
  3783. /**
  3784. * @event selectionchange
  3785. * Fires when the selection changes
  3786. * @param {SelectionModel} this
  3787. */
  3788. 'selectionchange',
  3789. /**
  3790. * @event beforerowselect
  3791. * Fires before a row is selected, return false to cancel the selection.
  3792. * @param {SelectionModel} this
  3793. * @param {Number} rowIndex The index to be selected
  3794. * @param {Boolean} keepExisting False if other selections will be cleared
  3795. * @param {Record} record The record to be selected
  3796. */
  3797. 'beforerowselect',
  3798. /**
  3799. * @event rowselect
  3800. * Fires when a row is selected.
  3801. * @param {SelectionModel} this
  3802. * @param {Number} rowIndex The selected index
  3803. * @param {Ext.data.Record} r The selected record
  3804. */
  3805. 'rowselect',
  3806. /**
  3807. * @event rowdeselect
  3808. * Fires when a row is deselected. To prevent deselection
  3809. * {@link Ext.grid.AbstractSelectionModel#lock lock the selections}.
  3810. * @param {SelectionModel} this
  3811. * @param {Number} rowIndex
  3812. * @param {Record} record
  3813. */
  3814. 'rowdeselect'
  3815. );
  3816. Ext.grid.RowSelectionModel.superclass.constructor.call(this);
  3817. },
  3818. /**
  3819. * @cfg {Boolean} moveEditorOnEnter
  3820. * <tt>false</tt> to turn off moving the editor to the next row down when the enter key is pressed
  3821. * or the next row up when shift + enter keys are pressed.
  3822. */
  3823. // private
  3824. initEvents : function(){
  3825. if(!this.grid.enableDragDrop && !this.grid.enableDrag){
  3826. this.grid.on('rowmousedown', this.handleMouseDown, this);
  3827. }
  3828. this.rowNav = new Ext.KeyNav(this.grid.getGridEl(), {
  3829. 'up' : function(e){
  3830. if(!e.shiftKey || this.singleSelect){
  3831. this.selectPrevious(false);
  3832. }else if(this.last !== false && this.lastActive !== false){
  3833. var last = this.last;
  3834. this.selectRange(this.last, this.lastActive-1);
  3835. this.grid.getView().focusRow(this.lastActive);
  3836. if(last !== false){
  3837. this.last = last;
  3838. }
  3839. }else{
  3840. this.selectFirstRow();
  3841. }
  3842. },
  3843. 'down' : function(e){
  3844. if(!e.shiftKey || this.singleSelect){
  3845. this.selectNext(false);
  3846. }else if(this.last !== false && this.lastActive !== false){
  3847. var last = this.last;
  3848. this.selectRange(this.last, this.lastActive+1);
  3849. this.grid.getView().focusRow(this.lastActive);
  3850. if(last !== false){
  3851. this.last = last;
  3852. }
  3853. }else{
  3854. this.selectFirstRow();
  3855. }
  3856. },
  3857. scope: this
  3858. });
  3859. this.grid.getView().on({
  3860. scope: this,
  3861. refresh: this.onRefresh,
  3862. rowupdated: this.onRowUpdated,
  3863. rowremoved: this.onRemove
  3864. });
  3865. },
  3866. // private
  3867. onRefresh : function(){
  3868. var ds = this.grid.store, index;
  3869. var s = this.getSelections();
  3870. this.clearSelections(true);
  3871. for(var i = 0, len = s.length; i < len; i++){
  3872. var r = s[i];
  3873. if((index = ds.indexOfId(r.id)) != -1){
  3874. this.selectRow(index, true);
  3875. }
  3876. }
  3877. if(s.length != this.selections.getCount()){
  3878. this.fireEvent('selectionchange', this);
  3879. }
  3880. },
  3881. // private
  3882. onRemove : function(v, index, r){
  3883. if(this.selections.remove(r) !== false){
  3884. this.fireEvent('selectionchange', this);
  3885. }
  3886. },
  3887. // private
  3888. onRowUpdated : function(v, index, r){
  3889. if(this.isSelected(r)){
  3890. v.onRowSelect(index);
  3891. }
  3892. },
  3893. /**
  3894. * Select records.
  3895. * @param {Array} records The records to select
  3896. * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep existing selections
  3897. */
  3898. selectRecords : function(records, keepExisting){
  3899. if(!keepExisting){
  3900. this.clearSelections();
  3901. }
  3902. var ds = this.grid.store;
  3903. for(var i = 0, len = records.length; i < len; i++){
  3904. this.selectRow(ds.indexOf(records[i]), true);
  3905. }
  3906. },
  3907. /**
  3908. * Gets the number of selected rows.
  3909. * @return {Number}
  3910. */
  3911. getCount : function(){
  3912. return this.selections.length;
  3913. },
  3914. /**
  3915. * Selects the first row in the grid.
  3916. */
  3917. selectFirstRow : function(){
  3918. this.selectRow(0);
  3919. },
  3920. /**
  3921. * Select the last row.
  3922. * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep existing selections
  3923. */
  3924. selectLastRow : function(keepExisting){
  3925. this.selectRow(this.grid.store.getCount() - 1, keepExisting);
  3926. },
  3927. /**
  3928. * Selects the row immediately following the last selected row.
  3929. * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep existing selections
  3930. * @return {Boolean} <tt>true</tt> if there is a next row, else <tt>false</tt>
  3931. */
  3932. selectNext : function(keepExisting){
  3933. if(this.hasNext()){
  3934. this.selectRow(this.last+1, keepExisting);
  3935. this.grid.getView().focusRow(this.last);
  3936. return true;
  3937. }
  3938. return false;
  3939. },
  3940. /**
  3941. * Selects the row that precedes the last selected row.
  3942. * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep existing selections
  3943. * @return {Boolean} <tt>true</tt> if there is a previous row, else <tt>false</tt>
  3944. */
  3945. selectPrevious : function(keepExisting){
  3946. if(this.hasPrevious()){
  3947. this.selectRow(this.last-1, keepExisting);
  3948. this.grid.getView().focusRow(this.last);
  3949. return true;
  3950. }
  3951. return false;
  3952. },
  3953. /**
  3954. * Returns true if there is a next record to select
  3955. * @return {Boolean}
  3956. */
  3957. hasNext : function(){
  3958. return this.last !== false && (this.last+1) < this.grid.store.getCount();
  3959. },
  3960. /**
  3961. * Returns true if there is a previous record to select
  3962. * @return {Boolean}
  3963. */
  3964. hasPrevious : function(){
  3965. return !!this.last;
  3966. },
  3967. /**
  3968. * Returns the selected records
  3969. * @return {Array} Array of selected records
  3970. */
  3971. getSelections : function(){
  3972. return [].concat(this.selections.items);
  3973. },
  3974. /**
  3975. * Returns the first selected record.
  3976. * @return {Record}
  3977. */
  3978. getSelected : function(){
  3979. return this.selections.itemAt(0);
  3980. },
  3981. /**
  3982. * Calls the passed function with each selection. If the function returns
  3983. * <tt>false</tt>, iteration is stopped and this function returns
  3984. * <tt>false</tt>. Otherwise it returns <tt>true</tt>.
  3985. * @param {Function} fn The function to call upon each iteration. It is passed the selected {@link Ext.data.Record Record}.
  3986. * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this RowSelectionModel.
  3987. * @return {Boolean} true if all selections were iterated
  3988. */
  3989. each : function(fn, scope){
  3990. var s = this.getSelections();
  3991. for(var i = 0, len = s.length; i < len; i++){
  3992. if(fn.call(scope || this, s[i], i) === false){
  3993. return false;
  3994. }
  3995. }
  3996. return true;
  3997. },
  3998. /**
  3999. * Clears all selections if the selection model
  4000. * {@link Ext.grid.AbstractSelectionModel#isLocked is not locked}.
  4001. * @param {Boolean} fast (optional) <tt>true</tt> to bypass the
  4002. * conditional checks and events described in {@link #deselectRow}.
  4003. */
  4004. clearSelections : function(fast){
  4005. if(this.isLocked()){
  4006. return;
  4007. }
  4008. if(fast !== true){
  4009. var ds = this.grid.store;
  4010. var s = this.selections;
  4011. s.each(function(r){
  4012. this.deselectRow(ds.indexOfId(r.id));
  4013. }, this);
  4014. s.clear();
  4015. }else{
  4016. this.selections.clear();
  4017. }
  4018. this.last = false;
  4019. },
  4020. /**
  4021. * Selects all rows if the selection model
  4022. * {@link Ext.grid.AbstractSelectionModel#isLocked is not locked}.
  4023. */
  4024. selectAll : function(){
  4025. if(this.isLocked()){
  4026. return;
  4027. }
  4028. this.selections.clear();
  4029. for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
  4030. this.selectRow(i, true);
  4031. }
  4032. },
  4033. /**
  4034. * Returns <tt>true</tt> if there is a selection.
  4035. * @return {Boolean}
  4036. */
  4037. hasSelection : function(){
  4038. return this.selections.length > 0;
  4039. },
  4040. /**
  4041. * Returns <tt>true</tt> if the specified row is selected.
  4042. * @param {Number/Record} index The record or index of the record to check
  4043. * @return {Boolean}
  4044. */
  4045. isSelected : function(index){
  4046. var r = Ext.isNumber(index) ? this.grid.store.getAt(index) : index;
  4047. return (r && this.selections.key(r.id) ? true : false);
  4048. },
  4049. /**
  4050. * Returns <tt>true</tt> if the specified record id is selected.
  4051. * @param {String} id The id of record to check
  4052. * @return {Boolean}
  4053. */
  4054. isIdSelected : function(id){
  4055. return (this.selections.key(id) ? true : false);
  4056. },
  4057. // private
  4058. handleMouseDown : function(g, rowIndex, e){
  4059. if(e.button !== 0 || this.isLocked()){
  4060. return;
  4061. }
  4062. var view = this.grid.getView();
  4063. if(e.shiftKey && !this.singleSelect && this.last !== false){
  4064. var last = this.last;
  4065. this.selectRange(last, rowIndex, e.ctrlKey);
  4066. this.last = last; // reset the last
  4067. view.focusRow(rowIndex);
  4068. }else{
  4069. var isSelected = this.isSelected(rowIndex);
  4070. if(e.ctrlKey && isSelected){
  4071. this.deselectRow(rowIndex);
  4072. }else if(!isSelected || this.getCount() > 1){
  4073. this.selectRow(rowIndex, e.ctrlKey || e.shiftKey);
  4074. view.focusRow(rowIndex);
  4075. }
  4076. }
  4077. },
  4078. /**
  4079. * Selects multiple rows.
  4080. * @param {Array} rows Array of the indexes of the row to select
  4081. * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep
  4082. * existing selections (defaults to <tt>false</tt>)
  4083. */
  4084. selectRows : function(rows, keepExisting){
  4085. if(!keepExisting){
  4086. this.clearSelections();
  4087. }
  4088. for(var i = 0, len = rows.length; i < len; i++){
  4089. this.selectRow(rows[i], true);
  4090. }
  4091. },
  4092. /**
  4093. * Selects a range of rows if the selection model
  4094. * {@link Ext.grid.AbstractSelectionModel#isLocked is not locked}.
  4095. * All rows in between startRow and endRow are also selected.
  4096. * @param {Number} startRow The index of the first row in the range
  4097. * @param {Number} endRow The index of the last row in the range
  4098. * @param {Boolean} keepExisting (optional) True to retain existing selections
  4099. */
  4100. selectRange : function(startRow, endRow, keepExisting){
  4101. var i;
  4102. if(this.isLocked()){
  4103. return;
  4104. }
  4105. if(!keepExisting){
  4106. this.clearSelections();
  4107. }
  4108. if(startRow <= endRow){
  4109. for(i = startRow; i <= endRow; i++){
  4110. this.selectRow(i, true);
  4111. }
  4112. }else{
  4113. for(i = startRow; i >= endRow; i--){
  4114. this.selectRow(i, true);
  4115. }
  4116. }
  4117. },
  4118. /**
  4119. * Deselects a range of rows if the selection model
  4120. * {@link Ext.grid.AbstractSelectionModel#isLocked is not locked}.
  4121. * All rows in between startRow and endRow are also deselected.
  4122. * @param {Number} startRow The index of the first row in the range
  4123. * @param {Number} endRow The index of the last row in the range
  4124. */
  4125. deselectRange : function(startRow, endRow, preventViewNotify){
  4126. if(this.isLocked()){
  4127. return;
  4128. }
  4129. for(var i = startRow; i <= endRow; i++){
  4130. this.deselectRow(i, preventViewNotify);
  4131. }
  4132. },
  4133. /**
  4134. * Selects a row. Before selecting a row, checks if the selection model
  4135. * {@link Ext.grid.AbstractSelectionModel#isLocked is locked} and fires the
  4136. * {@link #beforerowselect} event. If these checks are satisfied the row
  4137. * will be selected and followed up by firing the {@link #rowselect} and
  4138. * {@link #selectionchange} events.
  4139. * @param {Number} row The index of the row to select
  4140. * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep existing selections
  4141. * @param {Boolean} preventViewNotify (optional) Specify <tt>true</tt> to
  4142. * prevent notifying the view (disables updating the selected appearance)
  4143. */
  4144. selectRow : function(index, keepExisting, preventViewNotify){
  4145. if(this.isLocked() || (index < 0 || index >= this.grid.store.getCount()) || (keepExisting && this.isSelected(index))){
  4146. return;
  4147. }
  4148. var r = this.grid.store.getAt(index);
  4149. if(r && this.fireEvent('beforerowselect', this, index, keepExisting, r) !== false){
  4150. if(!keepExisting || this.singleSelect){
  4151. this.clearSelections();
  4152. }
  4153. this.selections.add(r);
  4154. this.last = this.lastActive = index;
  4155. if(!preventViewNotify){
  4156. this.grid.getView().onRowSelect(index);
  4157. }
  4158. this.fireEvent('rowselect', this, index, r);
  4159. this.fireEvent('selectionchange', this);
  4160. }
  4161. },
  4162. /**
  4163. * Deselects a row. Before deselecting a row, checks if the selection model
  4164. * {@link Ext.grid.AbstractSelectionModel#isLocked is locked}.
  4165. * If this check is satisfied the row will be deselected and followed up by
  4166. * firing the {@link #rowdeselect} and {@link #selectionchange} events.
  4167. * @param {Number} row The index of the row to deselect
  4168. * @param {Boolean} preventViewNotify (optional) Specify <tt>true</tt> to
  4169. * prevent notifying the view (disables updating the selected appearance)
  4170. */
  4171. deselectRow : function(index, preventViewNotify){
  4172. if(this.isLocked()){
  4173. return;
  4174. }
  4175. if(this.last == index){
  4176. this.last = false;
  4177. }
  4178. if(this.lastActive == index){
  4179. this.lastActive = false;
  4180. }
  4181. var r = this.grid.store.getAt(index);
  4182. if(r){
  4183. this.selections.remove(r);
  4184. if(!preventViewNotify){
  4185. this.grid.getView().onRowDeselect(index);
  4186. }
  4187. this.fireEvent('rowdeselect', this, index, r);
  4188. this.fireEvent('selectionchange', this);
  4189. }
  4190. },
  4191. // private
  4192. restoreLast : function(){
  4193. if(this._last){
  4194. this.last = this._last;
  4195. }
  4196. },
  4197. // private
  4198. acceptsNav : function(row, col, cm){
  4199. return !cm.isHidden(col) && cm.isCellEditable(col, row);
  4200. },
  4201. // private
  4202. onEditorKey : function(field, e){
  4203. var k = e.getKey(),
  4204. newCell,
  4205. g = this.grid,
  4206. last = g.lastEdit,
  4207. ed = g.activeEditor,
  4208. ae, last, r, c;
  4209. var shift = e.shiftKey;
  4210. if(k == e.TAB){
  4211. e.stopEvent();
  4212. ed.completeEdit();
  4213. if(shift){
  4214. newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
  4215. }else{
  4216. newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
  4217. }
  4218. }else if(k == e.ENTER){
  4219. if(this.moveEditorOnEnter !== false){
  4220. if(shift){
  4221. newCell = g.walkCells(last.row - 1, last.col, -1, this.acceptsNav, this);
  4222. }else{
  4223. newCell = g.walkCells(last.row + 1, last.col, 1, this.acceptsNav, this);
  4224. }
  4225. }
  4226. }
  4227. if(newCell){
  4228. r = newCell[0];
  4229. c = newCell[1];
  4230. if(last.row != r){
  4231. this.selectRow(r); // *** highlight newly-selected cell and update selection
  4232. }
  4233. if(g.isEditor && g.editing){ // *** handle tabbing while editorgrid is in edit mode
  4234. ae = g.activeEditor;
  4235. if(ae && ae.field.triggerBlur){
  4236. // *** if activeEditor is a TriggerField, explicitly call its triggerBlur() method
  4237. ae.field.triggerBlur();
  4238. }
  4239. }
  4240. g.startEditing(r, c);
  4241. }
  4242. },
  4243. destroy : function(){
  4244. if(this.rowNav){
  4245. this.rowNav.disable();
  4246. this.rowNav = null;
  4247. }
  4248. Ext.grid.RowSelectionModel.superclass.destroy.call(this);
  4249. }
  4250. });/**
  4251. * @class Ext.grid.Column
  4252. * <p>This class encapsulates column configuration data to be used in the initialization of a
  4253. * {@link Ext.grid.ColumnModel ColumnModel}.</p>
  4254. * <p>While subclasses are provided to render data in different ways, this class renders a passed
  4255. * data field unchanged and is usually used for textual columns.</p>
  4256. */
  4257. Ext.grid.Column = Ext.extend(Object, {
  4258. /**
  4259. * @cfg {Boolean} editable Optional. Defaults to <tt>true</tt>, enabling the configured
  4260. * <tt>{@link #editor}</tt>. Set to <tt>false</tt> to initially disable editing on this column.
  4261. * The initial configuration may be dynamically altered using
  4262. * {@link Ext.grid.ColumnModel}.{@link Ext.grid.ColumnModel#setEditable setEditable()}.
  4263. */
  4264. /**
  4265. * @cfg {String} id Optional. A name which identifies this column (defaults to the column's initial
  4266. * ordinal position.) The <tt>id</tt> is used to create a CSS <b>class</b> name which is applied to all
  4267. * table cells (including headers) in that column (in this context the <tt>id</tt> does not need to be
  4268. * unique). The class name takes the form of <pre>x-grid3-td-<b>id</b></pre>
  4269. * Header cells will also receive this class name, but will also have the class <pre>x-grid3-hd</pre>
  4270. * So, to target header cells, use CSS selectors such as:<pre>.x-grid3-hd-row .x-grid3-td-<b>id</b></pre>
  4271. * The {@link Ext.grid.GridPanel#autoExpandColumn} grid config option references the column via this
  4272. * unique identifier.
  4273. */
  4274. /**
  4275. * @cfg {String} header Optional. The header text to be used as innerHTML
  4276. * (html tags are accepted) to display in the Grid view. <b>Note</b>: to
  4277. * have a clickable header with no text displayed use <tt>'&#160;'</tt>.
  4278. */
  4279. /**
  4280. * @cfg {Boolean} groupable Optional. If the grid is being rendered by an {@link Ext.grid.GroupingView}, this option
  4281. * may be used to disable the header menu item to group by the column selected. Defaults to <tt>true</tt>,
  4282. * which enables the header menu group option. Set to <tt>false</tt> to disable (but still show) the
  4283. * group option in the header menu for the column. See also <code>{@link #groupName}</code>.
  4284. */
  4285. /**
  4286. * @cfg {String} groupName Optional. If the grid is being rendered by an {@link Ext.grid.GroupingView}, this option
  4287. * may be used to specify the text with which to prefix the group field value in the group header line.
  4288. * See also {@link #groupRenderer} and
  4289. * {@link Ext.grid.GroupingView}.{@link Ext.grid.GroupingView#showGroupName showGroupName}.
  4290. */
  4291. /**
  4292. * @cfg {Function} groupRenderer <p>Optional. If the grid is being rendered by an {@link Ext.grid.GroupingView}, this option
  4293. * may be used to specify the function used to format the grouping field value for display in the group
  4294. * {@link #groupName header}. If a <tt><b>groupRenderer</b></tt> is not specified, the configured
  4295. * <tt><b>{@link #renderer}</b></tt> will be called; if a <tt><b>{@link #renderer}</b></tt> is also not specified
  4296. * the new value of the group field will be used.</p>
  4297. * <p>The called function (either the <tt><b>groupRenderer</b></tt> or <tt><b>{@link #renderer}</b></tt>) will be
  4298. * passed the following parameters:
  4299. * <div class="mdetail-params"><ul>
  4300. * <li><b>v</b> : Object<p class="sub-desc">The new value of the group field.</p></li>
  4301. * <li><b>unused</b> : undefined<p class="sub-desc">Unused parameter.</p></li>
  4302. * <li><b>r</b> : Ext.data.Record<p class="sub-desc">The Record providing the data
  4303. * for the row which caused group change.</p></li>
  4304. * <li><b>rowIndex</b> : Number<p class="sub-desc">The row index of the Record which caused group change.</p></li>
  4305. * <li><b>colIndex</b> : Number<p class="sub-desc">The column index of the group field.</p></li>
  4306. * <li><b>ds</b> : Ext.data.Store<p class="sub-desc">The Store which is providing the data Model.</p></li>
  4307. * </ul></div></p>
  4308. * <p>The function should return a string value.</p>
  4309. */
  4310. /**
  4311. * @cfg {String} emptyGroupText Optional. If the grid is being rendered by an {@link Ext.grid.GroupingView}, this option
  4312. * may be used to specify the text to display when there is an empty group value. Defaults to the
  4313. * {@link Ext.grid.GroupingView}.{@link Ext.grid.GroupingView#emptyGroupText emptyGroupText}.
  4314. */
  4315. /**
  4316. * @cfg {String} dataIndex <p><b>Required</b>. The name of the field in the
  4317. * grid's {@link Ext.data.Store}'s {@link Ext.data.Record} definition from
  4318. * which to draw the column's value.</p>
  4319. */
  4320. /**
  4321. * @cfg {Number} width
  4322. * Optional. The initial width in pixels of the column.
  4323. * The width of each column can also be affected if any of the following are configured:
  4324. * <div class="mdetail-params"><ul>
  4325. * <li>{@link Ext.grid.GridPanel}.<tt>{@link Ext.grid.GridPanel#autoExpandColumn autoExpandColumn}</tt></li>
  4326. * <li>{@link Ext.grid.GridView}.<tt>{@link Ext.grid.GridView#forceFit forceFit}</tt>
  4327. * <div class="sub-desc">
  4328. * <p>By specifying <tt>forceFit:true</tt>, {@link #fixed non-fixed width} columns will be
  4329. * re-proportioned (based on the relative initial widths) to fill the width of the grid so
  4330. * that no horizontal scrollbar is shown.</p>
  4331. * </div></li>
  4332. * <li>{@link Ext.grid.GridView}.<tt>{@link Ext.grid.GridView#autoFill autoFill}</tt></li>
  4333. * <li>{@link Ext.grid.GridPanel}.<tt>{@link Ext.grid.GridPanel#minColumnWidth minColumnWidth}</tt></li>
  4334. * <br><p><b>Note</b>: when the width of each column is determined, a space on the right side
  4335. * is reserved for the vertical scrollbar. The
  4336. * {@link Ext.grid.GridView}.<tt>{@link Ext.grid.GridView#scrollOffset scrollOffset}</tt>
  4337. * can be modified to reduce or eliminate the reserved offset.</p>
  4338. */
  4339. /**
  4340. * @cfg {Boolean} sortable Optional. <tt>true</tt> if sorting is to be allowed on this column.
  4341. * Defaults to the value of the <code>{@link Ext.grid.ColumnModel#defaultSortable}</code> property.
  4342. * Whether local/remote sorting is used is specified in <code>{@link Ext.data.Store#remoteSort}</code>.
  4343. */
  4344. /**
  4345. * @cfg {Boolean} fixed Optional. <tt>true</tt> if the column width cannot be changed. Defaults to <tt>false</tt>.
  4346. */
  4347. /**
  4348. * @cfg {Boolean} resizable Optional. <tt>false</tt> to disable column resizing. Defaults to <tt>true</tt>.
  4349. */
  4350. /**
  4351. * @cfg {Boolean} menuDisabled Optional. <tt>true</tt> to disable the column menu. Defaults to <tt>false</tt>.
  4352. */
  4353. /**
  4354. * @cfg {Boolean} hidden
  4355. * Optional. <tt>true</tt> to initially hide this column. Defaults to <tt>false</tt>.
  4356. * A hidden column {@link Ext.grid.GridPanel#enableColumnHide may be shown via the header row menu}.
  4357. * If a column is never to be shown, simply do not include this column in the Column Model at all.
  4358. */
  4359. /**
  4360. * @cfg {String} tooltip Optional. A text string to use as the column header's tooltip. If Quicktips
  4361. * are enabled, this value will be used as the text of the quick tip, otherwise it will be set as the
  4362. * header's HTML title attribute. Defaults to ''.
  4363. */
  4364. /**
  4365. * @cfg {Mixed} renderer
  4366. * <p>For an alternative to specifying a renderer see <code>{@link #xtype}</code></p>
  4367. * <p>Optional. A renderer is an 'interceptor' method which can be used transform data (value,
  4368. * appearance, etc.) before it is rendered). This may be specified in either of three ways:
  4369. * <div class="mdetail-params"><ul>
  4370. * <li>A renderer function used to return HTML markup for a cell given the cell's data value.</li>
  4371. * <li>A string which references a property name of the {@link Ext.util.Format} class which
  4372. * provides a renderer function.</li>
  4373. * <li>An object specifying both the renderer function, and its execution scope (<tt><b>this</b></tt>
  4374. * reference) e.g.:<pre style="margin-left:1.2em"><code>
  4375. {
  4376. fn: this.gridRenderer,
  4377. scope: this
  4378. }
  4379. </code></pre></li></ul></div>
  4380. * If not specified, the default renderer uses the raw data value.</p>
  4381. * <p>For information about the renderer function (passed parameters, etc.), see
  4382. * {@link Ext.grid.ColumnModel#setRenderer}. An example of specifying renderer function inline:</p><pre><code>
  4383. var companyColumn = {
  4384. header: 'Company Name',
  4385. dataIndex: 'company',
  4386. renderer: function(value, metaData, record, rowIndex, colIndex, store) {
  4387. // provide the logic depending on business rules
  4388. // name of your own choosing to manipulate the cell depending upon
  4389. // the data in the underlying Record object.
  4390. if (value == 'whatever') {
  4391. //metaData.css : String : A CSS class name to add to the TD element of the cell.
  4392. //metaData.attr : String : An html attribute definition string to apply to
  4393. // the data container element within the table
  4394. // cell (e.g. 'style="color:red;"').
  4395. metaData.css = 'name-of-css-class-you-will-define';
  4396. }
  4397. return value;
  4398. }
  4399. }
  4400. * </code></pre>
  4401. * See also {@link #scope}.
  4402. */
  4403. /**
  4404. * @cfg {String} xtype Optional. A String which references a predefined {@link Ext.grid.Column} subclass
  4405. * type which is preconfigured with an appropriate <code>{@link #renderer}</code> to be easily
  4406. * configured into a ColumnModel. The predefined {@link Ext.grid.Column} subclass types are:
  4407. * <div class="mdetail-params"><ul>
  4408. * <li><b><tt>gridcolumn</tt></b> : {@link Ext.grid.Column} (<b>Default</b>)<p class="sub-desc"></p></li>
  4409. * <li><b><tt>booleancolumn</tt></b> : {@link Ext.grid.BooleanColumn}<p class="sub-desc"></p></li>
  4410. * <li><b><tt>numbercolumn</tt></b> : {@link Ext.grid.NumberColumn}<p class="sub-desc"></p></li>
  4411. * <li><b><tt>datecolumn</tt></b> : {@link Ext.grid.DateColumn}<p class="sub-desc"></p></li>
  4412. * <li><b><tt>templatecolumn</tt></b> : {@link Ext.grid.TemplateColumn}<p class="sub-desc"></p></li>
  4413. * </ul></div>
  4414. * <p>Configuration properties for the specified <code>xtype</code> may be specified with
  4415. * the Column configuration properties, for example:</p>
  4416. * <pre><code>
  4417. var grid = new Ext.grid.GridPanel({
  4418. ...
  4419. columns: [{
  4420. header: 'Last Updated',
  4421. dataIndex: 'lastChange',
  4422. width: 85,
  4423. sortable: true,
  4424. //renderer: Ext.util.Format.dateRenderer('m/d/Y'),
  4425. xtype: 'datecolumn', // use xtype instead of renderer
  4426. format: 'M/d/Y' // configuration property for {@link Ext.grid.DateColumn}
  4427. }, {
  4428. ...
  4429. }]
  4430. });
  4431. * </code></pre>
  4432. */
  4433. /**
  4434. * @cfg {Object} scope Optional. The scope (<tt><b>this</b></tt> reference) in which to execute the
  4435. * renderer. Defaults to the Column configuration object.
  4436. */
  4437. /**
  4438. * @cfg {String} align Optional. Set the CSS text-align property of the column. Defaults to undefined.
  4439. */
  4440. /**
  4441. * @cfg {String} css Optional. An inline style definition string which is applied to all table cells in the column
  4442. * (excluding headers). Defaults to undefined.
  4443. */
  4444. /**
  4445. * @cfg {Boolean} hideable Optional. Specify as <tt>false</tt> to prevent the user from hiding this column
  4446. * (defaults to true). To disallow column hiding globally for all columns in the grid, use
  4447. * {@link Ext.grid.GridPanel#enableColumnHide} instead.
  4448. */
  4449. /**
  4450. * @cfg {Ext.form.Field} editor Optional. The {@link Ext.form.Field} to use when editing values in this column
  4451. * if editing is supported by the grid. See <tt>{@link #editable}</tt> also.
  4452. */
  4453. /**
  4454. * @private
  4455. * @cfg {Boolean} isColumn
  4456. * Used by ColumnModel setConfig method to avoid reprocessing a Column
  4457. * if <code>isColumn</code> is not set ColumnModel will recreate a new Ext.grid.Column
  4458. * Defaults to true.
  4459. */
  4460. isColumn : true,
  4461. constructor : function(config){
  4462. Ext.apply(this, config);
  4463. if(Ext.isString(this.renderer)){
  4464. this.renderer = Ext.util.Format[this.renderer];
  4465. }else if(Ext.isObject(this.renderer)){
  4466. this.scope = this.renderer.scope;
  4467. this.renderer = this.renderer.fn;
  4468. }
  4469. if(!this.scope){
  4470. this.scope = this;
  4471. }
  4472. var ed = this.editor;
  4473. delete this.editor;
  4474. this.setEditor(ed);
  4475. },
  4476. /**
  4477. * Optional. A function which returns displayable data when passed the following parameters:
  4478. * <div class="mdetail-params"><ul>
  4479. * <li><b>value</b> : Object<p class="sub-desc">The data value for the cell.</p></li>
  4480. * <li><b>metadata</b> : Object<p class="sub-desc">An object in which you may set the following attributes:<ul>
  4481. * <li><b>css</b> : String<p class="sub-desc">A CSS class name to add to the cell's TD element.</p></li>
  4482. * <li><b>attr</b> : String<p class="sub-desc">An HTML attribute definition string to apply to the data container
  4483. * element <i>within</i> the table cell (e.g. 'style="color:red;"').</p></li></ul></p></li>
  4484. * <li><b>record</b> : Ext.data.record<p class="sub-desc">The {@link Ext.data.Record} from which the data was
  4485. * extracted.</p></li>
  4486. * <li><b>rowIndex</b> : Number<p class="sub-desc">Row index</p></li>
  4487. * <li><b>colIndex</b> : Number<p class="sub-desc">Column index</p></li>
  4488. * <li><b>store</b> : Ext.data.Store<p class="sub-desc">The {@link Ext.data.Store} object from which the Record
  4489. * was extracted.</p></li>
  4490. * </ul></div>
  4491. * @property renderer
  4492. * @type Function
  4493. */
  4494. renderer : function(value){
  4495. if(Ext.isString(value) && value.length < 1){
  4496. return '&#160;';
  4497. }
  4498. return value;
  4499. },
  4500. // private
  4501. getEditor: function(rowIndex){
  4502. return this.editable !== false ? this.editor : null;
  4503. },
  4504. /**
  4505. * Sets a new editor for this column.
  4506. * @param {Ext.Editor/Ext.form.Field} editor The editor to set
  4507. */
  4508. setEditor : function(editor){
  4509. var ed = this.editor;
  4510. if(ed){
  4511. if(ed.gridEditor){
  4512. ed.gridEditor.destroy();
  4513. delete ed.gridEditor;
  4514. }else{
  4515. ed.destroy();
  4516. }
  4517. }
  4518. this.editor = null;
  4519. if(editor){
  4520. //not an instance, create it
  4521. if(!editor.isXType){
  4522. editor = Ext.create(editor, 'textfield');
  4523. }
  4524. this.editor = editor;
  4525. }
  4526. },
  4527. /**
  4528. * Returns the {@link Ext.Editor editor} defined for this column that was created to wrap the {@link Ext.form.Field Field}
  4529. * used to edit the cell.
  4530. * @param {Number} rowIndex The row index
  4531. * @return {Ext.Editor}
  4532. */
  4533. getCellEditor: function(rowIndex){
  4534. var ed = this.getEditor(rowIndex);
  4535. if(ed){
  4536. if(!ed.startEdit){
  4537. if(!ed.gridEditor){
  4538. ed.gridEditor = new Ext.grid.GridEditor(ed);
  4539. }
  4540. ed = ed.gridEditor;
  4541. }
  4542. }
  4543. return ed;
  4544. }
  4545. });
  4546. /**
  4547. * @class Ext.grid.BooleanColumn
  4548. * @extends Ext.grid.Column
  4549. * <p>A Column definition class which renders boolean data fields. See the {@link Ext.grid.Column#xtype xtype}
  4550. * config option of {@link Ext.grid.Column} for more details.</p>
  4551. */
  4552. Ext.grid.BooleanColumn = Ext.extend(Ext.grid.Column, {
  4553. /**
  4554. * @cfg {String} trueText
  4555. * The string returned by the renderer when the column value is not falsey (defaults to <tt>'true'</tt>).
  4556. */
  4557. trueText: 'true',
  4558. /**
  4559. * @cfg {String} falseText
  4560. * The string returned by the renderer when the column value is falsey (but not undefined) (defaults to
  4561. * <tt>'false'</tt>).
  4562. */
  4563. falseText: 'false',
  4564. /**
  4565. * @cfg {String} undefinedText
  4566. * The string returned by the renderer when the column value is undefined (defaults to <tt>'&#160;'</tt>).
  4567. */
  4568. undefinedText: '&#160;',
  4569. constructor: function(cfg){
  4570. Ext.grid.BooleanColumn.superclass.constructor.call(this, cfg);
  4571. var t = this.trueText, f = this.falseText, u = this.undefinedText;
  4572. this.renderer = function(v){
  4573. if(v === undefined){
  4574. return u;
  4575. }
  4576. if(!v || v === 'false'){
  4577. return f;
  4578. }
  4579. return t;
  4580. };
  4581. }
  4582. });
  4583. /**
  4584. * @class Ext.grid.NumberColumn
  4585. * @extends Ext.grid.Column
  4586. * <p>A Column definition class which renders a numeric data field according to a {@link #format} string. See the
  4587. * {@link Ext.grid.Column#xtype xtype} config option of {@link Ext.grid.Column} for more details.</p>
  4588. */
  4589. Ext.grid.NumberColumn = Ext.extend(Ext.grid.Column, {
  4590. /**
  4591. * @cfg {String} format
  4592. * A formatting string as used by {@link Ext.util.Format#number} to format a numeric value for this Column
  4593. * (defaults to <tt>'0,000.00'</tt>).
  4594. */
  4595. format : '0,000.00',
  4596. constructor: function(cfg){
  4597. Ext.grid.NumberColumn.superclass.constructor.call(this, cfg);
  4598. this.renderer = Ext.util.Format.numberRenderer(this.format);
  4599. }
  4600. });
  4601. /**
  4602. * @class Ext.grid.DateColumn
  4603. * @extends Ext.grid.Column
  4604. * <p>A Column definition class which renders a passed date according to the default locale, or a configured
  4605. * {@link #format}. See the {@link Ext.grid.Column#xtype xtype} config option of {@link Ext.grid.Column}
  4606. * for more details.</p>
  4607. */
  4608. Ext.grid.DateColumn = Ext.extend(Ext.grid.Column, {
  4609. /**
  4610. * @cfg {String} format
  4611. * A formatting string as used by {@link Date#format} to format a Date for this Column
  4612. * (defaults to <tt>'m/d/Y'</tt>).
  4613. */
  4614. format : 'm/d/Y',
  4615. constructor: function(cfg){
  4616. Ext.grid.DateColumn.superclass.constructor.call(this, cfg);
  4617. this.renderer = Ext.util.Format.dateRenderer(this.format);
  4618. }
  4619. });
  4620. /**
  4621. * @class Ext.grid.TemplateColumn
  4622. * @extends Ext.grid.Column
  4623. * <p>A Column definition class which renders a value by processing a {@link Ext.data.Record Record}'s
  4624. * {@link Ext.data.Record#data data} using a {@link #tpl configured} {@link Ext.XTemplate XTemplate}.
  4625. * See the {@link Ext.grid.Column#xtype xtype} config option of {@link Ext.grid.Column} for more
  4626. * details.</p>
  4627. */
  4628. Ext.grid.TemplateColumn = Ext.extend(Ext.grid.Column, {
  4629. /**
  4630. * @cfg {String/XTemplate} tpl
  4631. * An {@link Ext.XTemplate XTemplate}, or an XTemplate <i>definition string</i> to use to process a
  4632. * {@link Ext.data.Record Record}'s {@link Ext.data.Record#data data} to produce a column's rendered value.
  4633. */
  4634. constructor: function(cfg){
  4635. Ext.grid.TemplateColumn.superclass.constructor.call(this, cfg);
  4636. var tpl = (!Ext.isPrimitive(this.tpl) && this.tpl.compile) ? this.tpl : new Ext.XTemplate(this.tpl);
  4637. this.renderer = function(value, p, r){
  4638. return tpl.apply(r.data);
  4639. };
  4640. this.tpl = tpl;
  4641. }
  4642. });
  4643. /*
  4644. * @property types
  4645. * @type Object
  4646. * @member Ext.grid.Column
  4647. * @static
  4648. * <p>An object containing predefined Column classes keyed by a mnemonic code which may be referenced
  4649. * by the {@link Ext.grid.ColumnModel#xtype xtype} config option of ColumnModel.</p>
  4650. * <p>This contains the following properties</p><div class="mdesc-details"><ul>
  4651. * <li>gridcolumn : <b>{@link Ext.grid.Column Column constructor}</b></li>
  4652. * <li>booleancolumn : <b>{@link Ext.grid.BooleanColumn BooleanColumn constructor}</b></li>
  4653. * <li>numbercolumn : <b>{@link Ext.grid.NumberColumn NumberColumn constructor}</b></li>
  4654. * <li>datecolumn : <b>{@link Ext.grid.DateColumn DateColumn constructor}</b></li>
  4655. * <li>templatecolumn : <b>{@link Ext.grid.TemplateColumn TemplateColumn constructor}</b></li>
  4656. * </ul></div>
  4657. */
  4658. Ext.grid.Column.types = {
  4659. gridcolumn : Ext.grid.Column,
  4660. booleancolumn: Ext.grid.BooleanColumn,
  4661. numbercolumn: Ext.grid.NumberColumn,
  4662. datecolumn: Ext.grid.DateColumn,
  4663. templatecolumn: Ext.grid.TemplateColumn
  4664. };/**
  4665. * @class Ext.grid.RowNumberer
  4666. * This is a utility class that can be passed into a {@link Ext.grid.ColumnModel} as a column config that provides
  4667. * an automatic row numbering column.
  4668. * <br>Usage:<br>
  4669. <pre><code>
  4670. // This is a typical column config with the first column providing row numbers
  4671. var colModel = new Ext.grid.ColumnModel([
  4672. new Ext.grid.RowNumberer(),
  4673. {header: "Name", width: 80, sortable: true},
  4674. {header: "Code", width: 50, sortable: true},
  4675. {header: "Description", width: 200, sortable: true}
  4676. ]);
  4677. </code></pre>
  4678. * @constructor
  4679. * @param {Object} config The configuration options
  4680. */
  4681. Ext.grid.RowNumberer = Ext.extend(Object, {
  4682. /**
  4683. * @cfg {String} header Any valid text or HTML fragment to display in the header cell for the row
  4684. * number column (defaults to '').
  4685. */
  4686. header: "",
  4687. /**
  4688. * @cfg {Number} width The default width in pixels of the row number column (defaults to 23).
  4689. */
  4690. width: 23,
  4691. /**
  4692. * @cfg {Boolean} sortable True if the row number column is sortable (defaults to false).
  4693. * @hide
  4694. */
  4695. sortable: false,
  4696. constructor : function(config){
  4697. Ext.apply(this, config);
  4698. if(this.rowspan){
  4699. this.renderer = this.renderer.createDelegate(this);
  4700. }
  4701. },
  4702. // private
  4703. fixed:true,
  4704. hideable: false,
  4705. menuDisabled:true,
  4706. dataIndex: '',
  4707. id: 'numberer',
  4708. rowspan: undefined,
  4709. // private
  4710. renderer : function(v, p, record, rowIndex){
  4711. if(this.rowspan){
  4712. p.cellAttr = 'rowspan="'+this.rowspan+'"';
  4713. }
  4714. return rowIndex+1;
  4715. }
  4716. });/**
  4717. * @class Ext.grid.CheckboxSelectionModel
  4718. * @extends Ext.grid.RowSelectionModel
  4719. * A custom selection model that renders a column of checkboxes that can be toggled to select or deselect rows.
  4720. * @constructor
  4721. * @param {Object} config The configuration options
  4722. */
  4723. Ext.grid.CheckboxSelectionModel = Ext.extend(Ext.grid.RowSelectionModel, {
  4724. /**
  4725. * @cfg {Boolean} checkOnly <tt>true</tt> if rows can only be selected by clicking on the
  4726. * checkbox column (defaults to <tt>false</tt>).
  4727. */
  4728. /**
  4729. * @cfg {String} header Any valid text or HTML fragment to display in the header cell for the
  4730. * checkbox column. Defaults to:<pre><code>
  4731. * '&lt;div class="x-grid3-hd-checker">&#38;#160;&lt;/div>'</tt>
  4732. * </code></pre>
  4733. * The default CSS class of <tt>'x-grid3-hd-checker'</tt> displays a checkbox in the header
  4734. * and provides support for automatic check all/none behavior on header click. This string
  4735. * can be replaced by any valid HTML fragment, including a simple text string (e.g.,
  4736. * <tt>'Select Rows'</tt>), but the automatic check all/none behavior will only work if the
  4737. * <tt>'x-grid3-hd-checker'</tt> class is supplied.
  4738. */
  4739. header : '<div class="x-grid3-hd-checker">&#160;</div>',
  4740. /**
  4741. * @cfg {Number} width The default width in pixels of the checkbox column (defaults to <tt>20</tt>).
  4742. */
  4743. width : 20,
  4744. /**
  4745. * @cfg {Boolean} sortable <tt>true</tt> if the checkbox column is sortable (defaults to
  4746. * <tt>false</tt>).
  4747. */
  4748. sortable : false,
  4749. // private
  4750. menuDisabled : true,
  4751. fixed : true,
  4752. hideable: false,
  4753. dataIndex : '',
  4754. id : 'checker',
  4755. constructor : function(){
  4756. Ext.grid.CheckboxSelectionModel.superclass.constructor.apply(this, arguments);
  4757. if(this.checkOnly){
  4758. this.handleMouseDown = Ext.emptyFn;
  4759. }
  4760. },
  4761. // private
  4762. initEvents : function(){
  4763. Ext.grid.CheckboxSelectionModel.superclass.initEvents.call(this);
  4764. this.grid.on('render', function(){
  4765. var view = this.grid.getView();
  4766. view.mainBody.on('mousedown', this.onMouseDown, this);
  4767. Ext.fly(view.innerHd).on('mousedown', this.onHdMouseDown, this);
  4768. }, this);
  4769. },
  4770. // If handleMouseDown was called from another event (enableDragDrop), set a flag so
  4771. // onMouseDown does not process it a second time
  4772. handleMouseDown : function() {
  4773. Ext.grid.CheckboxSelectionModel.superclass.handleMouseDown.apply(this, arguments);
  4774. this.mouseHandled = true;
  4775. },
  4776. // private
  4777. onMouseDown : function(e, t){
  4778. if(e.button === 0 && t.className == 'x-grid3-row-checker'){ // Only fire if left-click
  4779. e.stopEvent();
  4780. var row = e.getTarget('.x-grid3-row');
  4781. // mouseHandled flag check for a duplicate selection (handleMouseDown) call
  4782. if(!this.mouseHandled && row){
  4783. var index = row.rowIndex;
  4784. if(this.isSelected(index)){
  4785. this.deselectRow(index);
  4786. }else{
  4787. this.selectRow(index, true);
  4788. }
  4789. }
  4790. }
  4791. this.mouseHandled = false;
  4792. },
  4793. // private
  4794. onHdMouseDown : function(e, t){
  4795. if(t.className == 'x-grid3-hd-checker'){
  4796. e.stopEvent();
  4797. var hd = Ext.fly(t.parentNode);
  4798. var isChecked = hd.hasClass('x-grid3-hd-checker-on');
  4799. if(isChecked){
  4800. hd.removeClass('x-grid3-hd-checker-on');
  4801. this.clearSelections();
  4802. }else{
  4803. hd.addClass('x-grid3-hd-checker-on');
  4804. this.selectAll();
  4805. }
  4806. }
  4807. },
  4808. // private
  4809. renderer : function(v, p, record){
  4810. return '<div class="x-grid3-row-checker">&#160;</div>';
  4811. }
  4812. });