CellModel.html 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  5. <title>The source code</title>
  6. <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
  7. <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
  8. <style type="text/css">
  9. .highlight { display: block; background-color: #ddd; }
  10. </style>
  11. <script type="text/javascript">
  12. function highlight() {
  13. document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
  14. }
  15. </script>
  16. </head>
  17. <body onload="prettyPrint(); highlight();">
  18. <pre class="prettyprint lang-js"><span id='Ext-selection-CellModel'>/**
  19. </span> *
  20. */
  21. Ext.define('Ext.selection.CellModel', {
  22. extend: 'Ext.selection.Model',
  23. alias: 'selection.cellmodel',
  24. requires: ['Ext.util.KeyNav'],
  25. isCellModel: true,
  26. <span id='Ext-selection-CellModel-cfg-enableKeyNav'> /**
  27. </span> * @cfg {Boolean} enableKeyNav
  28. * Turns on/off keyboard navigation within the grid.
  29. */
  30. enableKeyNav: true,
  31. <span id='Ext-selection-CellModel-cfg-preventWrap'> /**
  32. </span> * @cfg {Boolean} preventWrap
  33. * Set this configuration to true to prevent wrapping around of selection as
  34. * a user navigates to the first or last column.
  35. */
  36. preventWrap: false,
  37. // private property to use when firing a deselect when no old selection exists.
  38. noSelection: {
  39. row: -1,
  40. column: -1
  41. },
  42. constructor: function() {
  43. this.addEvents(
  44. <span id='Ext-selection-CellModel-event-deselect'> /**
  45. </span> * @event deselect
  46. * Fired after a cell is deselected
  47. * @param {Ext.selection.CellModel} this
  48. * @param {Ext.data.Model} record The record of the deselected cell
  49. * @param {Number} row The row index deselected
  50. * @param {Number} column The column index deselected
  51. */
  52. 'deselect',
  53. <span id='Ext-selection-CellModel-event-select'> /**
  54. </span> * @event select
  55. * Fired after a cell is selected
  56. * @param {Ext.selection.CellModel} this
  57. * @param {Ext.data.Model} record The record of the selected cell
  58. * @param {Number} row The row index selected
  59. * @param {Number} column The column index selected
  60. */
  61. 'select'
  62. );
  63. this.callParent(arguments);
  64. },
  65. bindComponent: function(view) {
  66. var me = this,
  67. grid = view.ownerCt;
  68. me.primaryView = view;
  69. me.views = me.views || [];
  70. me.views.push(view);
  71. me.bindStore(view.getStore(), true);
  72. view.on({
  73. cellmousedown: me.onMouseDown,
  74. refresh: me.onViewRefresh,
  75. scope: me
  76. });
  77. if (grid.optimizedColumnMove !== false) {
  78. grid.on('columnmove', me.onColumnMove, me);
  79. }
  80. if (me.enableKeyNav) {
  81. me.initKeyNav(view);
  82. }
  83. },
  84. initKeyNav: function(view) {
  85. var me = this;
  86. if (!view.rendered) {
  87. view.on('render', Ext.Function.bind(me.initKeyNav, me, [view], 0), me, {single: true});
  88. return;
  89. }
  90. view.el.set({
  91. tabIndex: -1
  92. });
  93. // view.el has tabIndex -1 to allow for
  94. // keyboard events to be passed to it.
  95. me.keyNav = new Ext.util.KeyNav({
  96. target: view.el,
  97. ignoreInputFields: true,
  98. up: me.onKeyUp,
  99. down: me.onKeyDown,
  100. right: me.onKeyRight,
  101. left: me.onKeyLeft,
  102. tab: me.onKeyTab,
  103. scope: me
  104. });
  105. },
  106. getHeaderCt: function() {
  107. var selection = this.getCurrentPosition(),
  108. view = selection ? selection.view : this.primaryView;
  109. return view.headerCt;
  110. },
  111. onKeyUp: function(e, t) {
  112. this.keyNavigation = true;
  113. this.move('up', e);
  114. this.keyNavigation = false;
  115. },
  116. onKeyDown: function(e, t) {
  117. this.keyNavigation = true;
  118. this.move('down', e);
  119. this.keyNavigation = false;
  120. },
  121. onKeyLeft: function(e, t) {
  122. this.keyNavigation = true;
  123. this.move('left', e);
  124. this.keyNavigation = false;
  125. },
  126. onKeyRight: function(e, t) {
  127. this.keyNavigation = true;
  128. this.move('right', e);
  129. this.keyNavigation = false;
  130. },
  131. move: function(dir, e) {
  132. var me = this,
  133. pos = me.getCurrentPosition(),
  134. // Calculate the new row and column position
  135. newPos = pos.view.walkCells(pos, dir, e, me.preventWrap);
  136. // If walk was successful, select new Position
  137. if (newPos) {
  138. newPos.view = pos.view;
  139. return me.setCurrentPosition(newPos);
  140. }
  141. // &lt;debug&gt;
  142. // Enforce code correctness in unbuilt source.
  143. return null;
  144. // &lt;/debug&gt;
  145. },
  146. <span id='Ext-selection-CellModel-method-getCurrentPosition'> /**
  147. </span> * Returns the current position in the format {row: row, column: column}
  148. */
  149. getCurrentPosition: function() {
  150. return this.selection;
  151. },
  152. <span id='Ext-selection-CellModel-method-setCurrentPosition'> /**
  153. </span> * Sets the current position
  154. * @param {Object} position The position to set.
  155. */
  156. setCurrentPosition: function(pos) {
  157. var me = this;
  158. // onSelectChange uses lastSelection and nextSelection
  159. me.lastSelection = me.selection;
  160. if (me.selection) {
  161. me.onCellDeselect(me.selection);
  162. }
  163. if (pos) {
  164. me.nextSelection = new me.Selection(me);
  165. me.nextSelection.setPosition(pos);
  166. me.onCellSelect(me.nextSelection);
  167. // Deselect triggered by new selection will kill the selection property, so restore it here.
  168. return me.selection = me.nextSelection;
  169. }
  170. // &lt;debug&gt;
  171. // Enforce code correctness in unbuilt source.
  172. return null;
  173. // &lt;/debug&gt;
  174. },
  175. // Keep selection model in consistent state upon record deletion.
  176. onStoreRemove: function(store, record, index) {
  177. var me = this,
  178. pos = me.getCurrentPosition();
  179. me.callParent(arguments);
  180. if (pos) {
  181. // Deleting the row containing the selection.
  182. // Attempt to reselect the same cell which has moved up if there is one
  183. if (pos.row == index) {
  184. if (index &lt; store.getCount() - 1) {
  185. pos.setPosition(index, pos.column);
  186. me.setCurrentPosition(pos);
  187. } else {
  188. delete me.selection;
  189. }
  190. }
  191. // Deleting a row before the selection.
  192. // Move the selection up by one row
  193. else if (index &lt; pos.row) {
  194. pos.setPosition(pos.row - 1, pos.column);
  195. me.setCurrentPosition(pos);
  196. }
  197. }
  198. },
  199. <span id='Ext-selection-CellModel-method-onMouseDown'> /**
  200. </span> * Set the current position based on where the user clicks.
  201. * @private
  202. */
  203. onMouseDown: function(view, cell, cellIndex, record, row, rowIndex, e) {
  204. this.setCurrentPosition({
  205. view: view,
  206. row: rowIndex,
  207. column: cellIndex
  208. });
  209. },
  210. // notify the view that the cell has been selected to update the ui
  211. // appropriately and bring the cell into focus
  212. onCellSelect: function(position, supressEvent) {
  213. if (position &amp;&amp; position.row !== undefined &amp;&amp; position.row &gt; -1) {
  214. this.doSelect(position.view.getStore().getAt(position.row), /*keepExisting*/false, supressEvent);
  215. }
  216. },
  217. // notify view that the cell has been deselected to update the ui
  218. // appropriately
  219. onCellDeselect: function(position, supressEvent) {
  220. if (position &amp;&amp; position.row !== undefined) {
  221. this.doDeselect(position.view.getStore().getAt(position.row), supressEvent);
  222. }
  223. },
  224. onSelectChange: function(record, isSelected, suppressEvent, commitFn) {
  225. var me = this,
  226. pos,
  227. eventName,
  228. view;
  229. if (isSelected) {
  230. pos = me.nextSelection;
  231. eventName = 'select';
  232. } else {
  233. pos = me.lastSelection || me.noSelection;
  234. eventName = 'deselect';
  235. }
  236. // CellModel may be shared between two sides of a Lockable.
  237. // The position must include a reference to the view in which the selection is current.
  238. // Ensure we use the view specifiied by the position.
  239. view = pos.view || me.primaryView;
  240. if ((suppressEvent || me.fireEvent('before' + eventName, me, record, pos.row, pos.column)) !== false &amp;&amp;
  241. commitFn() !== false) {
  242. if (isSelected) {
  243. view.onCellSelect(pos);
  244. view.onCellFocus(pos);
  245. } else {
  246. view.onCellDeselect(pos);
  247. delete me.selection;
  248. }
  249. if (!suppressEvent) {
  250. me.fireEvent(eventName, me, record, pos.row, pos.column);
  251. }
  252. }
  253. },
  254. // Tab key from the View's KeyNav, *not* from an editor.
  255. onKeyTab: function(e, t) {
  256. var me = this,
  257. editingPlugin = me.getCurrentPosition().view.editingPlugin;
  258. // If we were in editing mode, but just focused on a non-editable cell, behave as if we tabbed off an editable field
  259. if (editingPlugin &amp;&amp; me.wasEditing) {
  260. me.onEditorTab(editingPlugin, e)
  261. } else {
  262. me.move(e.shiftKey ? 'left' : 'right', e);
  263. }
  264. },
  265. onEditorTab: function(editingPlugin, e) {
  266. var me = this,
  267. direction = e.shiftKey ? 'left' : 'right',
  268. position = me.move(direction, e);
  269. // Navigation had somewhere to go.... not hit the buffers.
  270. if (position) {
  271. // If we were able to begin editing clear the wasEditing flag. It gets set during navigation off an active edit.
  272. if (editingPlugin.startEditByPosition(position)) {
  273. me.wasEditing = false;
  274. }
  275. // If we could not continue editing...
  276. // Set a flag that we should go back into editing mode upon next onKeyTab call
  277. else {
  278. me.wasEditing = true;
  279. if (!position.columnHeader.dataIndex) {
  280. me.onEditorTab(editingPlugin, e);
  281. }
  282. }
  283. }
  284. },
  285. refresh: function() {
  286. var pos = this.getCurrentPosition(),
  287. selRowIdx;
  288. // Synchronize the current position's row with the row of the last selected record.
  289. if (pos &amp;&amp; (selRowIdx = this.store.indexOf(this.selected.last())) !== -1) {
  290. pos.row = selRowIdx;
  291. }
  292. },
  293. <span id='Ext-selection-CellModel-method-onColumnMove'> /**
  294. </span> * @private
  295. * When grid uses {@link Ext.panel.Table#optimizedColumnMove optimizedColumnMove} (the default), this is added as a
  296. * {@link Ext.panel.Table#columnmove columnmove} handler to correctly maintain the
  297. * selected column using the same column header.
  298. *
  299. * If optimizedColumnMove === false, (which some grid Features set) then the view is refreshed,
  300. * so this is not added as a handler because the selected column.
  301. */
  302. onColumnMove: function(headerCt, header, fromIdx, toIdx) {
  303. var grid = headerCt.up('tablepanel');
  304. if (grid) {
  305. this.onViewRefresh(grid.view);
  306. }
  307. },
  308. onViewRefresh: function(view) {
  309. var me = this,
  310. pos = me.getCurrentPosition(),
  311. headerCt = view.headerCt,
  312. record, columnHeader;
  313. // Re-establish selection of the same cell coordinate.
  314. // DO NOT fire events because the selected
  315. if (pos &amp;&amp; pos.view === view) {
  316. record = pos.record;
  317. columnHeader = pos.columnHeader;
  318. // After a refresh, recreate the selection using the same record and grid column as before
  319. if (!columnHeader.isDescendantOf(headerCt)) {
  320. // column header is not a child of the header container
  321. // this happens when the grid is reconfigured with new columns
  322. // make a best effor to select something by matching on id, then text, then dataIndex
  323. columnHeader = headerCt.queryById(columnHeader.id) ||
  324. headerCt.down('[text=&quot;' + columnHeader.text + '&quot;]') ||
  325. headerCt.down('[dataIndex=&quot;' + columnHeader.dataIndex + '&quot;]');
  326. }
  327. // If we have a columnHeader (either the column header that already exists in
  328. // the headerCt, or a suitable match that was found after reconfiguration)
  329. // AND the record still exists in the store (or a record matching the id of
  330. // the previously selected record) We are ok to go ahead and set the selection
  331. if (columnHeader &amp;&amp; (view.store.indexOfId(record.getId()) !== -1)) {
  332. me.setCurrentPosition({
  333. row: record,
  334. column: columnHeader,
  335. view: view
  336. });
  337. }
  338. }
  339. },
  340. selectByPosition: function(position) {
  341. this.setCurrentPosition(position);
  342. }
  343. }, function() {
  344. // Encapsulate a single selection position.
  345. // Maintains { row: n, column: n, record: r, columnHeader: c}
  346. var Selection = this.prototype.Selection = function(model) {
  347. this.model = model;
  348. };
  349. // Selection row/record &amp; column/columnHeader
  350. Selection.prototype.setPosition = function(row, col) {
  351. var me = this,
  352. view;
  353. // We were passed {row: 1, column: 2, view: myView}
  354. if (arguments.length === 1) {
  355. // SelectionModel is shared between both sides of a locking grid.
  356. // It can be positioned on either view.
  357. if (row.view) {
  358. me.view = view = row.view;
  359. }
  360. col = row.column;
  361. row = row.row;
  362. }
  363. // If setting the position without specifying a view, and the position is already without a view
  364. // use the owning Model's primary view
  365. if (!view) {
  366. me.view = view = me.model.primaryView;
  367. }
  368. // Row index passed
  369. if (typeof row === 'number') {
  370. me.row = row;
  371. me.record = view.store.getAt(row);
  372. }
  373. // row is a Record
  374. else if (row.isModel) {
  375. me.record = row;
  376. me.row = view.indexOf(row);
  377. }
  378. // row is a grid row
  379. else if (row.tagName) {
  380. me.record = view.getRecord(row);
  381. me.row = view.indexOf(me.record);
  382. }
  383. // column index passed
  384. if (typeof col === 'number') {
  385. me.column = col;
  386. me.columnHeader = view.getHeaderAtIndex(col);
  387. }
  388. // col is a column Header
  389. else {
  390. me.columnHeader = col;
  391. me.column = col.getIndex();
  392. }
  393. return me;
  394. }
  395. });</pre>
  396. </body>
  397. </html>