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.

620 lines
22 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.CellSelectionModel
  9. * @extends Ext.grid.AbstractSelectionModel
  10. * This class provides the basic implementation for <i>single</i> <b>cell</b> selection in a grid.
  11. * The object stored as the selection contains the following properties:
  12. * <div class="mdetail-params"><ul>
  13. * <li><b>cell</b> : see {@link #getSelectedCell}
  14. * <li><b>record</b> : Ext.data.record The {@link Ext.data.Record Record}
  15. * which provides the data for the row containing the selection</li>
  16. * </ul></div>
  17. * @constructor
  18. * @param {Object} config The object containing the configuration of this model.
  19. */
  20. Ext.grid.CellSelectionModel = Ext.extend(Ext.grid.AbstractSelectionModel, {
  21. constructor : function(config){
  22. Ext.apply(this, config);
  23. this.selection = null;
  24. this.addEvents(
  25. /**
  26. * @event beforecellselect
  27. * Fires before a cell is selected, return false to cancel the selection.
  28. * @param {SelectionModel} this
  29. * @param {Number} rowIndex The selected row index
  30. * @param {Number} colIndex The selected cell index
  31. */
  32. "beforecellselect",
  33. /**
  34. * @event cellselect
  35. * Fires when a cell is selected.
  36. * @param {SelectionModel} this
  37. * @param {Number} rowIndex The selected row index
  38. * @param {Number} colIndex The selected cell index
  39. */
  40. "cellselect",
  41. /**
  42. * @event selectionchange
  43. * Fires when the active selection changes.
  44. * @param {SelectionModel} this
  45. * @param {Object} selection null for no selection or an object with two properties
  46. * <div class="mdetail-params"><ul>
  47. * <li><b>cell</b> : see {@link #getSelectedCell}
  48. * <li><b>record</b> : Ext.data.record<p class="sub-desc">The {@link Ext.data.Record Record}
  49. * which provides the data for the row containing the selection</p></li>
  50. * </ul></div>
  51. */
  52. "selectionchange"
  53. );
  54. Ext.grid.CellSelectionModel.superclass.constructor.call(this);
  55. },
  56. /** @ignore */
  57. initEvents : function(){
  58. this.grid.on('cellmousedown', this.handleMouseDown, this);
  59. this.grid.on(Ext.EventManager.useKeydown ? 'keydown' : 'keypress', this.handleKeyDown, this);
  60. this.grid.getView().on({
  61. scope: this,
  62. refresh: this.onViewChange,
  63. rowupdated: this.onRowUpdated,
  64. beforerowremoved: this.clearSelections,
  65. beforerowsinserted: this.clearSelections
  66. });
  67. if(this.grid.isEditor){
  68. this.grid.on('beforeedit', this.beforeEdit, this);
  69. }
  70. },
  71. //private
  72. beforeEdit : function(e){
  73. this.select(e.row, e.column, false, true, e.record);
  74. },
  75. //private
  76. onRowUpdated : function(v, index, r){
  77. if(this.selection && this.selection.record == r){
  78. v.onCellSelect(index, this.selection.cell[1]);
  79. }
  80. },
  81. //private
  82. onViewChange : function(){
  83. this.clearSelections(true);
  84. },
  85. /**
  86. * Returns an array containing the row and column indexes of the currently selected cell
  87. * (e.g., [0, 0]), or null if none selected. The array has elements:
  88. * <div class="mdetail-params"><ul>
  89. * <li><b>rowIndex</b> : Number<p class="sub-desc">The index of the selected row</p></li>
  90. * <li><b>cellIndex</b> : Number<p class="sub-desc">The index of the selected cell.
  91. * Due to possible column reordering, the cellIndex should <b>not</b> be used as an
  92. * index into the Record's data. Instead, use the cellIndex to determine the <i>name</i>
  93. * of the selected cell and use the field name to retrieve the data value from the record:<pre><code>
  94. // get name
  95. var fieldName = grid.getColumnModel().getDataIndex(cellIndex);
  96. // get data value based on name
  97. var data = record.get(fieldName);
  98. * </code></pre></p></li>
  99. * </ul></div>
  100. * @return {Array} An array containing the row and column indexes of the selected cell, or null if none selected.
  101. */
  102. getSelectedCell : function(){
  103. return this.selection ? this.selection.cell : null;
  104. },
  105. /**
  106. * If anything is selected, clears all selections and fires the selectionchange event.
  107. * @param {Boolean} preventNotify <tt>true</tt> to prevent the gridview from
  108. * being notified about the change.
  109. */
  110. clearSelections : function(preventNotify){
  111. var s = this.selection;
  112. if(s){
  113. if(preventNotify !== true){
  114. this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
  115. }
  116. this.selection = null;
  117. this.fireEvent("selectionchange", this, null);
  118. }
  119. },
  120. /**
  121. * Returns <tt>true</tt> if there is a selection.
  122. * @return {Boolean}
  123. */
  124. hasSelection : function(){
  125. return this.selection ? true : false;
  126. },
  127. /** @ignore */
  128. handleMouseDown : function(g, row, cell, e){
  129. if(e.button !== 0 || this.isLocked()){
  130. return;
  131. }
  132. this.select(row, cell);
  133. },
  134. /**
  135. * Selects a cell. Before selecting a cell, fires the
  136. * {@link #beforecellselect} event. If this check is satisfied the cell
  137. * will be selected and followed up by firing the {@link #cellselect} and
  138. * {@link #selectionchange} events.
  139. * @param {Number} rowIndex The index of the row to select
  140. * @param {Number} colIndex The index of the column to select
  141. * @param {Boolean} preventViewNotify (optional) Specify <tt>true</tt> to
  142. * prevent notifying the view (disables updating the selected appearance)
  143. * @param {Boolean} preventFocus (optional) Whether to prevent the cell at
  144. * the specified rowIndex / colIndex from being focused.
  145. * @param {Ext.data.Record} r (optional) The record to select
  146. */
  147. select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
  148. if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
  149. this.clearSelections();
  150. r = r || this.grid.store.getAt(rowIndex);
  151. this.selection = {
  152. record : r,
  153. cell : [rowIndex, colIndex]
  154. };
  155. if(!preventViewNotify){
  156. var v = this.grid.getView();
  157. v.onCellSelect(rowIndex, colIndex);
  158. if(preventFocus !== true){
  159. v.focusCell(rowIndex, colIndex);
  160. }
  161. }
  162. this.fireEvent("cellselect", this, rowIndex, colIndex);
  163. this.fireEvent("selectionchange", this, this.selection);
  164. }
  165. },
  166. //private
  167. isSelectable : function(rowIndex, colIndex, cm){
  168. return !cm.isHidden(colIndex);
  169. },
  170. // private
  171. onEditorKey: function(field, e){
  172. if(e.getKey() == e.TAB){
  173. this.handleKeyDown(e);
  174. }
  175. },
  176. /** @ignore */
  177. handleKeyDown : function(e){
  178. if(!e.isNavKeyPress()){
  179. return;
  180. }
  181. var k = e.getKey(),
  182. g = this.grid,
  183. s = this.selection,
  184. sm = this,
  185. walk = function(row, col, step){
  186. return g.walkCells(
  187. row,
  188. col,
  189. step,
  190. g.isEditor && g.editing ? sm.acceptsNav : sm.isSelectable, // *** handle tabbing while editorgrid is in edit mode
  191. sm
  192. );
  193. },
  194. cell, newCell, r, c, ae;
  195. switch(k){
  196. case e.ESC:
  197. case e.PAGE_UP:
  198. case e.PAGE_DOWN:
  199. // do nothing
  200. break;
  201. default:
  202. // *** call e.stopEvent() only for non ESC, PAGE UP/DOWN KEYS
  203. e.stopEvent();
  204. break;
  205. }
  206. if(!s){
  207. cell = walk(0, 0, 1); // *** use private walk() function defined above
  208. if(cell){
  209. this.select(cell[0], cell[1]);
  210. }
  211. return;
  212. }
  213. cell = s.cell; // currently selected cell
  214. r = cell[0]; // current row
  215. c = cell[1]; // current column
  216. switch(k){
  217. case e.TAB:
  218. if(e.shiftKey){
  219. newCell = walk(r, c - 1, -1);
  220. }else{
  221. newCell = walk(r, c + 1, 1);
  222. }
  223. break;
  224. case e.DOWN:
  225. newCell = walk(r + 1, c, 1);
  226. break;
  227. case e.UP:
  228. newCell = walk(r - 1, c, -1);
  229. break;
  230. case e.RIGHT:
  231. newCell = walk(r, c + 1, 1);
  232. break;
  233. case e.LEFT:
  234. newCell = walk(r, c - 1, -1);
  235. break;
  236. case e.ENTER:
  237. if (g.isEditor && !g.editing) {
  238. g.startEditing(r, c);
  239. return;
  240. }
  241. break;
  242. }
  243. if(newCell){
  244. // *** reassign r & c variables to newly-selected cell's row and column
  245. r = newCell[0];
  246. c = newCell[1];
  247. this.select(r, c); // *** highlight newly-selected cell and update selection
  248. if(g.isEditor && g.editing){ // *** handle tabbing while editorgrid is in edit mode
  249. ae = g.activeEditor;
  250. if(ae && ae.field.triggerBlur){
  251. // *** if activeEditor is a TriggerField, explicitly call its triggerBlur() method
  252. ae.field.triggerBlur();
  253. }
  254. g.startEditing(r, c);
  255. }
  256. }
  257. },
  258. acceptsNav : function(row, col, cm){
  259. return !cm.isHidden(col) && cm.isCellEditable(col, row);
  260. }
  261. });/**
  262. * @class Ext.grid.EditorGridPanel
  263. * @extends Ext.grid.GridPanel
  264. * <p>This class extends the {@link Ext.grid.GridPanel GridPanel Class} to provide cell editing
  265. * on selected {@link Ext.grid.Column columns}. The editable columns are specified by providing
  266. * an {@link Ext.grid.ColumnModel#editor editor} in the {@link Ext.grid.Column column configuration}.</p>
  267. * <p>Editability of columns may be controlled programatically by inserting an implementation
  268. * of {@link Ext.grid.ColumnModel#isCellEditable isCellEditable} into the
  269. * {@link Ext.grid.ColumnModel ColumnModel}.</p>
  270. * <p>Editing is performed on the value of the <i>field</i> specified by the column's
  271. * <tt>{@link Ext.grid.ColumnModel#dataIndex dataIndex}</tt> in the backing {@link Ext.data.Store Store}
  272. * (so if you are using a {@link Ext.grid.ColumnModel#setRenderer renderer} in order to display
  273. * transformed data, this must be accounted for).</p>
  274. * <p>If a value-to-description mapping is used to render a column, then a {@link Ext.form.Field#ComboBox ComboBox}
  275. * which uses the same {@link Ext.form.Field#valueField value}-to-{@link Ext.form.Field#displayFieldField description}
  276. * mapping would be an appropriate editor.</p>
  277. * If there is a more complex mismatch between the visible data in the grid, and the editable data in
  278. * the {@link Edt.data.Store Store}, then code to transform the data both before and after editing can be
  279. * injected using the {@link #beforeedit} and {@link #afteredit} events.
  280. * @constructor
  281. * @param {Object} config The config object
  282. * @xtype editorgrid
  283. */
  284. Ext.grid.EditorGridPanel = Ext.extend(Ext.grid.GridPanel, {
  285. /**
  286. * @cfg {Number} clicksToEdit
  287. * <p>The number of clicks on a cell required to display the cell's editor (defaults to 2).</p>
  288. * <p>Setting this option to 'auto' means that mousedown <i>on the selected cell</i> starts
  289. * editing that cell.</p>
  290. */
  291. clicksToEdit: 2,
  292. /**
  293. * @cfg {Boolean} forceValidation
  294. * True to force validation even if the value is unmodified (defaults to false)
  295. */
  296. forceValidation: false,
  297. // private
  298. isEditor : true,
  299. // private
  300. detectEdit: false,
  301. /**
  302. * @cfg {Boolean} autoEncode
  303. * True to automatically HTML encode and decode values pre and post edit (defaults to false)
  304. */
  305. autoEncode : false,
  306. /**
  307. * @cfg {Boolean} trackMouseOver @hide
  308. */
  309. // private
  310. trackMouseOver: false, // causes very odd FF errors
  311. // private
  312. initComponent : function(){
  313. Ext.grid.EditorGridPanel.superclass.initComponent.call(this);
  314. if(!this.selModel){
  315. /**
  316. * @cfg {Object} selModel Any subclass of AbstractSelectionModel that will provide the selection model for
  317. * the grid (defaults to {@link Ext.grid.CellSelectionModel} if not specified).
  318. */
  319. this.selModel = new Ext.grid.CellSelectionModel();
  320. }
  321. this.activeEditor = null;
  322. this.addEvents(
  323. /**
  324. * @event beforeedit
  325. * Fires before cell editing is triggered. The edit event object has the following properties <br />
  326. * <ul style="padding:5px;padding-left:16px;">
  327. * <li>grid - This grid</li>
  328. * <li>record - The record being edited</li>
  329. * <li>field - The field name being edited</li>
  330. * <li>value - The value for the field being edited.</li>
  331. * <li>row - The grid row index</li>
  332. * <li>column - The grid column index</li>
  333. * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
  334. * </ul>
  335. * @param {Object} e An edit event (see above for description)
  336. */
  337. "beforeedit",
  338. /**
  339. * @event afteredit
  340. * Fires after a cell is edited. The edit event object has the following properties <br />
  341. * <ul style="padding:5px;padding-left:16px;">
  342. * <li>grid - This grid</li>
  343. * <li>record - The record being edited</li>
  344. * <li>field - The field name being edited</li>
  345. * <li>value - The value being set</li>
  346. * <li>originalValue - The original value for the field, before the edit.</li>
  347. * <li>row - The grid row index</li>
  348. * <li>column - The grid column index</li>
  349. * </ul>
  350. *
  351. * <pre><code>
  352. grid.on('afteredit', afterEdit, this );
  353. function afterEdit(e) {
  354. // execute an XHR to send/commit data to the server, in callback do (if successful):
  355. e.record.commit();
  356. };
  357. * </code></pre>
  358. * @param {Object} e An edit event (see above for description)
  359. */
  360. "afteredit",
  361. /**
  362. * @event validateedit
  363. * Fires after a cell is edited, but before the value is set in the record. Return false
  364. * to cancel the change. The edit event object has the following properties <br />
  365. * <ul style="padding:5px;padding-left:16px;">
  366. * <li>grid - This grid</li>
  367. * <li>record - The record being edited</li>
  368. * <li>field - The field name being edited</li>
  369. * <li>value - The value being set</li>
  370. * <li>originalValue - The original value for the field, before the edit.</li>
  371. * <li>row - The grid row index</li>
  372. * <li>column - The grid column index</li>
  373. * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
  374. * </ul>
  375. * Usage example showing how to remove the red triangle (dirty record indicator) from some
  376. * records (not all). By observing the grid's validateedit event, it can be cancelled if
  377. * the edit occurs on a targeted row (for example) and then setting the field's new value
  378. * in the Record directly:
  379. * <pre><code>
  380. grid.on('validateedit', function(e) {
  381. var myTargetRow = 6;
  382. if (e.row == myTargetRow) {
  383. e.cancel = true;
  384. e.record.data[e.field] = e.value;
  385. }
  386. });
  387. * </code></pre>
  388. * @param {Object} e An edit event (see above for description)
  389. */
  390. "validateedit"
  391. );
  392. },
  393. // private
  394. initEvents : function(){
  395. Ext.grid.EditorGridPanel.superclass.initEvents.call(this);
  396. this.getGridEl().on('mousewheel', this.stopEditing.createDelegate(this, [true]), this);
  397. this.on('columnresize', this.stopEditing, this, [true]);
  398. if(this.clicksToEdit == 1){
  399. this.on("cellclick", this.onCellDblClick, this);
  400. }else {
  401. var view = this.getView();
  402. if(this.clicksToEdit == 'auto' && view.mainBody){
  403. view.mainBody.on('mousedown', this.onAutoEditClick, this);
  404. }
  405. this.on('celldblclick', this.onCellDblClick, this);
  406. }
  407. },
  408. onResize : function(){
  409. Ext.grid.EditorGridPanel.superclass.onResize.apply(this, arguments);
  410. var ae = this.activeEditor;
  411. if(this.editing && ae){
  412. ae.realign(true);
  413. }
  414. },
  415. // private
  416. onCellDblClick : function(g, row, col){
  417. this.startEditing(row, col);
  418. },
  419. // private
  420. onAutoEditClick : function(e, t){
  421. if(e.button !== 0){
  422. return;
  423. }
  424. var row = this.view.findRowIndex(t),
  425. col = this.view.findCellIndex(t);
  426. if(row !== false && col !== false){
  427. this.stopEditing();
  428. if(this.selModel.getSelectedCell){ // cell sm
  429. var sc = this.selModel.getSelectedCell();
  430. if(sc && sc[0] === row && sc[1] === col){
  431. this.startEditing(row, col);
  432. }
  433. }else{
  434. if(this.selModel.isSelected(row)){
  435. this.startEditing(row, col);
  436. }
  437. }
  438. }
  439. },
  440. // private
  441. onEditComplete : function(ed, value, startValue){
  442. this.editing = false;
  443. this.lastActiveEditor = this.activeEditor;
  444. this.activeEditor = null;
  445. var r = ed.record,
  446. field = this.colModel.getDataIndex(ed.col);
  447. value = this.postEditValue(value, startValue, r, field);
  448. if(this.forceValidation === true || String(value) !== String(startValue)){
  449. var e = {
  450. grid: this,
  451. record: r,
  452. field: field,
  453. originalValue: startValue,
  454. value: value,
  455. row: ed.row,
  456. column: ed.col,
  457. cancel:false
  458. };
  459. if(this.fireEvent("validateedit", e) !== false && !e.cancel && String(value) !== String(startValue)){
  460. r.set(field, e.value);
  461. delete e.cancel;
  462. this.fireEvent("afteredit", e);
  463. }
  464. }
  465. this.view.focusCell(ed.row, ed.col);
  466. },
  467. /**
  468. * Starts editing the specified for the specified row/column
  469. * @param {Number} rowIndex
  470. * @param {Number} colIndex
  471. */
  472. startEditing : function(row, col){
  473. this.stopEditing();
  474. if(this.colModel.isCellEditable(col, row)){
  475. this.view.ensureVisible(row, col, true);
  476. var r = this.store.getAt(row),
  477. field = this.colModel.getDataIndex(col),
  478. e = {
  479. grid: this,
  480. record: r,
  481. field: field,
  482. value: r.data[field],
  483. row: row,
  484. column: col,
  485. cancel:false
  486. };
  487. if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
  488. this.editing = true;
  489. var ed = this.colModel.getCellEditor(col, row);
  490. if(!ed){
  491. return;
  492. }
  493. if(!ed.rendered){
  494. ed.parentEl = this.view.getEditorParent(ed);
  495. ed.on({
  496. scope: this,
  497. render: {
  498. fn: function(c){
  499. c.field.focus(false, true);
  500. },
  501. single: true,
  502. scope: this
  503. },
  504. specialkey: function(field, e){
  505. this.getSelectionModel().onEditorKey(field, e);
  506. },
  507. complete: this.onEditComplete,
  508. canceledit: this.stopEditing.createDelegate(this, [true])
  509. });
  510. }
  511. Ext.apply(ed, {
  512. row : row,
  513. col : col,
  514. record : r
  515. });
  516. this.lastEdit = {
  517. row: row,
  518. col: col
  519. };
  520. this.activeEditor = ed;
  521. // Set the selectSameEditor flag if we are reusing the same editor again and
  522. // need to prevent the editor from firing onBlur on itself.
  523. ed.selectSameEditor = (this.activeEditor == this.lastActiveEditor);
  524. var v = this.preEditValue(r, field);
  525. ed.startEdit(this.view.getCell(row, col).firstChild, Ext.isDefined(v) ? v : '');
  526. // Clear the selectSameEditor flag
  527. (function(){
  528. delete ed.selectSameEditor;
  529. }).defer(50);
  530. }
  531. }
  532. },
  533. // private
  534. preEditValue : function(r, field){
  535. var value = r.data[field];
  536. return this.autoEncode && Ext.isString(value) ? Ext.util.Format.htmlDecode(value) : value;
  537. },
  538. // private
  539. postEditValue : function(value, originalValue, r, field){
  540. return this.autoEncode && Ext.isString(value) ? Ext.util.Format.htmlEncode(value) : value;
  541. },
  542. /**
  543. * Stops any active editing
  544. * @param {Boolean} cancel (optional) True to cancel any changes
  545. */
  546. stopEditing : function(cancel){
  547. if(this.editing){
  548. // Store the lastActiveEditor to check if it is changing
  549. var ae = this.lastActiveEditor = this.activeEditor;
  550. if(ae){
  551. ae[cancel === true ? 'cancelEdit' : 'completeEdit']();
  552. this.view.focusCell(ae.row, ae.col);
  553. }
  554. this.activeEditor = null;
  555. }
  556. this.editing = false;
  557. }
  558. });
  559. Ext.reg('editorgrid', Ext.grid.EditorGridPanel);// private
  560. // This is a support class used internally by the Grid components
  561. Ext.grid.GridEditor = function(field, config){
  562. Ext.grid.GridEditor.superclass.constructor.call(this, field, config);
  563. field.monitorTab = false;
  564. };
  565. Ext.extend(Ext.grid.GridEditor, Ext.Editor, {
  566. alignment: "tl-tl",
  567. autoSize: "width",
  568. hideEl : false,
  569. cls: "x-small-editor x-grid-editor",
  570. shim:false,
  571. shadow:false
  572. });