| /******************************************************************************* |
| * Copyright (c) 2014-2018 BSI Business Systems Integration AG. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * BSI Business Systems Integration AG - initial API and implementation |
| ******************************************************************************/ |
| scout.CellEditorPopup = function() { |
| scout.CellEditorPopup.parent.call(this); |
| this.table = null; |
| this.column = null; |
| this.row = null; |
| this.cell = null; |
| this._pendingCompleteCellEdit = null; |
| this._keyStrokeHandler = this._onKeyStroke.bind(this); |
| }; |
| scout.inherits(scout.CellEditorPopup, scout.Popup); |
| |
| scout.CellEditorPopup.prototype._init = function(options) { |
| options.scrollType = options.scrollType || 'position'; |
| scout.CellEditorPopup.parent.prototype._init.call(this, options); |
| |
| this.table = options.column.table; |
| this.link(this.cell.field); |
| }; |
| |
| scout.CellEditorPopup.prototype._createLayout = function() { |
| return new scout.CellEditorPopupLayout(this); |
| }; |
| |
| /** |
| * @override |
| */ |
| scout.CellEditorPopup.prototype._initKeyStrokeContext = function() { |
| scout.CellEditorPopup.parent.prototype._initKeyStrokeContext.call(this); |
| |
| this.keyStrokeContext.registerKeyStroke([ |
| new scout.CellEditorCompleteEditKeyStroke(this), |
| new scout.CellEditorTabKeyStroke(this) |
| ]); |
| |
| // Don't propagate up/down key strokes to the table, because the table |
| // would call event.preventDefault() in its own "stop propagation |
| // interceptor" and the cursor would not move in multi line string fields. |
| this.keyStrokeContext.registerStopPropagationInterceptor(function(event) { |
| if (scout.isOneOf(event.which, scout.keys.UP, scout.keys.DOWN)) { |
| event.stopPropagation(); |
| } |
| }); |
| }; |
| |
| /** |
| * @override Popup.js |
| */ |
| scout.CellEditorPopup.prototype._createCloseKeyStroke = function() { |
| return new scout.CellEditorCancelEditKeyStroke(this); |
| }; |
| |
| /** |
| * @override |
| */ |
| scout.CellEditorPopup.prototype._open = function($parent, event) { |
| this.render($parent, event); |
| this.position(); |
| this.pack(); |
| }; |
| |
| scout.CellEditorPopup.prototype._render = function() { |
| scout.CellEditorPopup.parent.prototype._render.call(this); |
| |
| var firstCell = this.table.visibleColumns().indexOf(this.column) === 0; |
| this.$container.addClass('cell-editor-popup'); |
| this.$container.data('popup', this); |
| if (firstCell) { |
| this.$container.addClass('first'); |
| } |
| |
| var field = this.cell.field; |
| field.mode = scout.FormField.Mode.CELLEDITOR; // hint that this field is used within a cell-editor |
| field.render(); |
| field.prepareForCellEdit({ |
| firstCell: firstCell |
| }); |
| |
| // Make sure cell content is not visible while the editor is open (especially necessary for transparent editors like checkboxes) |
| this.$anchor.css('visibility', 'hidden'); |
| |
| this._rowOrderChangedFunc = function(event) { |
| if (event.animating) { |
| // row is only set while animating |
| if (event.row === this.row) { |
| this.position(); |
| } |
| } else { |
| this.position(); |
| } |
| }.bind(this); |
| this.table.on('rowOrderChanged', this._rowOrderChangedFunc); |
| // Set table style to focused, so that it looks as it still has the focus. |
| // This prevents flickering if the cell editor gets opened, especially when tabbing to the next cell editor. |
| if (this.table.enabled) { |
| this.table.$container.addClass('focused'); |
| } |
| this.session.keyStrokeManager.on('keyStroke', this._keyStrokeHandler); |
| }; |
| |
| scout.CellEditorPopup.prototype._postRender = function() { |
| scout.CellEditorPopup.parent.prototype._postRender.call(this); // installs the focus context for this popup |
| |
| // If applicable, invoke the field's function 'onCellEditorRendered' to signal the cell-editor to be rendered. |
| var field = this.cell.field; |
| if (field.onCellEditorRendered) { |
| field.onCellEditorRendered({ |
| openFieldPopup: this.table.openFieldPopupOnCellEdit, |
| cellEditorPopup: this |
| }); |
| } |
| }; |
| |
| scout.CellEditorPopup.prototype._remove = function() { |
| scout.CellEditorPopup.parent.prototype._remove.call(this); // uninstalls the focus context for this popup |
| |
| this.session.keyStrokeManager.off('keyStroke', this._keyStrokeHandler); |
| this.table.off('rowOrderChanged', this._rowOrderChangedFunc); |
| // table may have been removed in the meantime |
| if (this.table.rendered) { |
| this.table.$container.removeClass('focused'); |
| } |
| this.$anchor.css('visibility', ''); |
| }; |
| |
| scout.CellEditorPopup.prototype.position = function() { |
| var cellBounds, rowBounds, |
| $tableData = this.table.$data, |
| $row = this.row.$row, |
| $cell = this.$anchor, |
| insetsLeft = $tableData.cssPxValue('padding-left') + $row.cssBorderLeftWidth(); |
| |
| cellBounds = scout.graphics.bounds($cell); |
| cellBounds.x += $cell.cssMarginX(); // first cell popup has a negative left margin |
| rowBounds = scout.graphics.bounds($row); |
| rowBounds.y += $row.cssMarginY(); // row has a negative top margin |
| this.setLocation(new scout.Point(insetsLeft + cellBounds.x, $tableData.scrollTop() + rowBounds.y)); |
| }; |
| |
| /** |
| * @returns {Promise} resolved when acceptInput is performed on the editor field |
| */ |
| scout.CellEditorPopup.prototype.completeEdit = function(waitForAcceptInput) { |
| if (this._pendingCompleteCellEdit) { |
| // Make sure complete cell edit does not get sent twice since it will lead to exceptions. This may happen if user clicks very fast multiple times. |
| return this._pendingCompleteCellEdit; |
| } |
| |
| // There is no blur event when the popup gets closed -> trigger blur so that the field may react (accept display text, close popups etc.) |
| // When acceptInput returns a promise, we must wait until input is accepted |
| // Otherwise call completeEdit immediately, also call it immediately if waitForAcceptInput is false (see _onKeyStroke) |
| var field = this.cell.field; |
| var acceptInputPromise = field.acceptInput(); |
| if (!acceptInputPromise || !scout.nvl(waitForAcceptInput, true)) { |
| this._pendingCompleteCellEdit = $.resolvedPromise(); |
| this.table.completeCellEdit(); |
| } else { |
| this._pendingCompleteCellEdit = acceptInputPromise.then(function() { |
| this.table.completeCellEdit(); |
| }.bind(this)); |
| } |
| |
| this._pendingCompleteCellEdit.then(function() { |
| this._pendingCompleteCellEdit = null; |
| }.bind(this)); |
| |
| return this._pendingCompleteCellEdit; |
| }; |
| |
| scout.CellEditorPopup.prototype.isCompleteCellEditRequested = function() { |
| return !!this._pendingCompleteCellEdit; |
| }; |
| |
| scout.CellEditorPopup.prototype.cancelEdit = function() { |
| this.table.cancelCellEdit(); |
| this.remove(); |
| }; |
| |
| scout.CellEditorPopup.prototype._onMouseDownOutside = function(event) { |
| this.completeEdit(); |
| }; |
| |
| scout.CellEditorPopup.prototype._onKeyStroke = function(event) { |
| if (!this.session.keyStrokeManager.invokeAcceptInputOnActiveValueField(event.keyStroke, event.keyStrokeContext)) { |
| return; |
| } |
| if (this.$container.isOrHas(event.keyStrokeContext.$getScopeTarget())) { |
| // Don't interfere with key strokes of the popup or children of the popup (otherwise pressing enter would close both the popup and the form at once) |
| return; |
| } |
| // Make sure completeEdit is called immediately after calling acceptInput. |
| // Otherwise the key stroke will be executed before completing the edit which prevents the input from being saved |
| this.completeEdit(false); |
| }; |
| |
| scout.CellEditorPopup.prototype.waitForCompleteCellEdit = function() { |
| if (this._pendingCompleteCellEdit) { |
| return this._pendingCompleteCellEdit.promise(); |
| } |
| return $.resolvedPromise(); |
| }; |