/*
*************************************************************************
* The contents of this file are subject to the Openbravo Public License
* Version 1.1 (the "License"), being the Mozilla Public License
* Version 1.1 with a permitted attribution clause; you may not use this
* file except in compliance with the License. You may obtain a copy of
* the License at http://www.openbravo.com/legal/license.html
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
* License for the specific language governing rights and limitations
* under the License.
* The Original Code is Openbravo ERP.
* The Initial Developer of the Original Code is Openbravo SLU
* All portions are Copyright (C) 2010-2013 Openbravo SLU
* All Rights Reserved.
* Contributor(s): ______________________________________.
************************************************************************
*/
isc.ClassFactory.defineClass('OBViewGrid', isc.OBGrid);
isc.OBViewGrid.addClassProperties({
EDIT_LINK_FIELD_NAME: '_editLink',
//prevent the count operation on the server
NO_COUNT_PARAMETER: '_noCount',
// note following 2 values should be the same
// ListGrid._$ArrowUp and ListGrid._$ArrowDown
ARROW_UP_KEY_NAME: 'Arrow_Up',
ARROW_DOWN_KEY_NAME: 'Arrow_Down',
ERROR_MESSAGE_PROP: isc.OBViewGrid.ERROR_MESSAGE_PROP,
ICONS: {
PROGRESS: 0,
OPEN_IN_FORM: 1,
SEPARATOR1: 2,
EDIT_IN_GRID: 3,
CANCEL: 4,
SEPARATOR2: 5,
SAVE: 6
},
SUPPORTED_SUMMARY_FUNCTIONS: ['count', 'avg', 'min', 'max', 'sum']
});
if (!isc.Browser.isIE) {
isc.OBViewGrid.addProperties({
enforceVClipping: true // To avoid apply in IE, since it moves the grid row content to the top of each cell (issue 17884)
});
}
// = OBViewGrid =
// The OBViewGrid is the Openbravo specific subclass of the Smartclient
// ListGrid.
isc.OBViewGrid.addProperties({
// ** {{{ view }}} **
// The view member contains the pointer to the composite canvas which
// handles this form
// and the grid and other related components.
view: null,
// ** {{{ editGrid }}} **
// Controls if an edit link column is created in the grid, set to false to
// prevent this.
editGrid: true,
// ** {{{ editLinkFieldProperties }}} **
// The properties of the ListGridField created for the edit links.
editLinkFieldProperties: {
type: 'text',
canSort: false,
canReorder: false,
frozen: true,
canFreeze: false,
canEdit: false,
canGroupBy: false,
canHide: false,
showTitle: true,
title: ' ',
// autoFitWidth: true,
canDragResize: false,
canFilter: true,
autoExpand: false,
filterEditorType: 'StaticTextItem',
name: isc.OBViewGrid.EDIT_LINK_FIELD_NAME
},
editLinkColNum: -1,
// ** {{{ dataPageSize }}} **
// The data page size used for loading paged data from the server.
dataPageSize: 100,
fetchDelay: 500,
autoFitFieldWidths: true,
autoFitWidthApproach: 'title',
canAutoFitFields: false,
minFieldWidth: 75,
width: '100%',
height: '100%',
showSortArrow: 'field',
autoFetchTextMatchStyle: 'substring',
showFilterEditor: true,
canEdit: true,
alternateRecordStyles: true,
canReorderFields: true,
canFreezeFields: true,
canAddFormulaFields: true,
canAddSummaryFields: true,
canGroupBy: true,
showGroupSummaryInHeader: true,
showGroupSummary: true,
showGroupTitleColumn: false,
groupByMaxRecords: 1000,
selectionAppearance: 'checkbox',
arrowKeyAction: 'select',
useAllDataSourceFields: false,
editEvent: 'none',
showCellContextMenus: true,
canOpenRecordEditor: true,
showDetailFields: true,
showErrorIcons: false,
ungroupText: OB.I18N.getLabel('OBUIAPP_ungroup'),
groupByText: OB.I18N.getLabel('OBUIAPP_GroupBy'),
allowFilterExpressions: true,
showFilterExpressionLegendMenuItem: true,
// internal sc grid property, see the ListGrid source code
preserveEditsOnSetData: false,
// enabling this results in a slower user interaction
// it is better to allow fast grid interaction and if an error occurs
// dismiss any new records being edited and go back to the edit row
// which causes the error
// set to true to solve this issue:
// https://issues.openbravo.com/view.php?id=21352
waitForSave: true,
stopOnErrors: false,
confirmDiscardEdits: false,
canMultiSort: false,
emptyMessage: OB.I18N.getLabel('OBUISC_ListGrid.loadingDataMessage'),
discardEditsSaveButtonTitle: OB.I18N.getLabel('UINAVBA_Save'),
editPendingCSSText: null,
// commented out because of: https://issues.openbravo.com/view.php?id=16515
// default is much smaller which give smoother scrolling
// quickDrawAheadRatio: 1.0,
// drawAheadRatio: 2.0,
// see this discussion:
// http://forums.smartclient.com/showthread.php?t=16376
// scrollRedrawDelay: 20,
// note: don't set drawAllMaxCells too high as it results in extra reads
// of data, Smartclient will try to read until drawAllMaxCells has been
// reached
drawAllMaxCells: 0,
// the default is enabled which is a commonly used field
recordEnabledProperty: '_enabled',
// keeps track if we are in objectSelectionMode or in toggleSelectionMode
// objectSelectionMode = singleRecordSelection === true
singleRecordSelection: false,
// editing props
rowEndEditAction: 'next',
listEndEditAction: 'next',
fixedRecordHeights: true,
validateByCell: true,
currentEditColumnLayout: null,
recordBaseStyleProperty: '_recordStyle',
// set to false because of this: https://issues.openbravo.com/view.php?id=16509
modalEditing: false,
// set to true because if not all cols are drawn then when doing inline editing
// errors were reported for undrawn columns
// need to rework how the FormInitializationComponent sets the valuemap and defaultvalue
// for non-existing columns this should be stored somewhere, see this reply in
// the smartclient forum:
// http://forums.smartclient.com/showthread.php?p=63146
showAllColumns: true,
// showGridSummary: true,
timeFormatter: 'to24HourTime',
dataProperties: {
// this means that after an update/add the new/updated row does not fit
// in the current filter criteria then they are still shown
// note that if this is set to false that when using the _dummy criteria
// that the _dummy criteria can mean that new/updated records are not
// shown in the grid
neverDropUpdatedRows: true,
useClientFiltering: false,
useClientSorting: false,
fetchDelay: 300,
// overridden to update the context/request properties for the fetch
fetchRemoteData: function (serverCriteria, startRow, endRow) {
// clone to prevent side effects
var requestProperties = isc.clone(this.context);
this.grid.getFetchRequestParams(requestProperties.params);
return this.Super('fetchRemoteData', arguments);
},
clearLoadingMarkers: function (start, end) {
var j;
if (this.localData) {
for (j = start; j < end; j++) {
if (Array.isLoading(this.localData[j])) {
this.localData[j] = null;
}
}
}
},
// always return false otherwise sc switches to local mode
// which does not work correctly for when doing inserts in form mode
// at that point the grid.data.allRows is being used which results
// in mismatches with grid.data.localData, returning false here
// prevents allRows from being used. In our case we never really
// want to have all rows cached locally as we do all filtering
// server side.
allRowsCached: function () {
return false;
},
transformData: function (newData, dsResponse) {
var i, length, timeFields, responseToFilter;
// when the data is received from the datasource, time fields are formatted in UTC time. They have to be converted to local time
if (dsResponse && dsResponse.context && (dsResponse.context.operationType === 'fetch' || dsResponse.context.operationType === 'update' || dsResponse.context.operationType === 'add')) {
if (this.grid) {
newData = OB.Utilities.Date.convertUTCTimeToLocalTime(newData, this.grid.completeFields);
}
}
// only do this stuff for fetch operations, in other cases strange things
// happen as update/delete operations do not return the totalRows parameter
if (dsResponse && dsResponse.context && dsResponse.context.operationType !== 'fetch') {
return newData;
}
// correct the length if there is already data in the localData array
// only do this if filtering is not the origin action to the datasource request
// see issue https://issues.openbravo.com/view.php?id=23006
responseToFilter = false;
if (dsResponse.context && dsResponse.context._dsRequest && dsResponse.context._dsRequest.filtering) {
responseToFilter = true;
}
if (this.localData && !responseToFilter) {
length = this.localData.length;
for (i = dsResponse.endRow + 1; i < length; i++) {
if (!Array.isLoading(this.localData[i]) && this.localData[i]) {
dsResponse.totalRows = i + 1;
} else {
break;
}
}
// get rid of old loading markers, this has to be done explicitly
// as we can return another rowset than requested
// call with a delay otherwise the grid will keep requesting rows while processing the
// current rowset
this.delayCall('clearLoadingMarkers', [dsResponse.context.startRow, dsResponse.context.endRow], 100);
} else {
// Clear the filtering attribute from the context to prevent including it
// automatically in the following datasource requests
if (this.context) {
delete this.context.filtering;
}
}
if (this.localData && this.localData[dsResponse.totalRows]) {
this.localData[dsResponse.totalRows] = null;
}
return newData;
}
},
initWidget: function () {
var i, vwState;
// make a copy of the dataProperties otherwise we get
// change results that values of one grid are copied/coming back
// in other grids
this.dataProperties = isc.addProperties({}, this.dataProperties);
// override setSort to sort by group title when the grouped by
// column is clicked
this.groupTreeProperties = {
grid: this,
// always return all records
getRange: function (start, end) {
return this.getOpenList(
this.root, this.openDisplayNodeType, null, this.sortDirection, this.openListCriteria, null, true).slice(start, end);
},
setSort: function (sortSpecifier) {
var i, fld, sortSpec = isc.clone(sortSpecifier),
flds = this.grid.getAllFields(),
groupByFields = this.grid.getGroupByFields();
if (groupByFields && sortSpec && sortSpec[0]) {
for (i = 0; i < flds.length; i++) {
fld = flds[i];
if (groupByFields.contains(fld.name) && (fld.name === sortSpec[0].property || fld.displayField === sortSpec[0].property)) {
sortSpec[0].property = 'groupValue';
break;
}
}
}
return this.Super('setSort', [sortSpec]);
}
};
// re-use getCellValue to handle count and related functions
this.summaryRowProperties = {
showRecordComponents: false,
cellHoverHTML: this.cellHoverHTML,
getCellAlign: function (record, rowNum, colNum) {
var fld = this.getFields()[colNum],
isRTL = this.isRTL(),
func = this.getGridSummaryFunction(fld),
isSummary = record && (record[this.groupSummaryRecordProperty] || record[this.gridSummaryRecordProperty]);
// the count of a character column should also be right aligned
if (isSummary && func === 'count') {
return isRTL ? isc.Canvas.LEFT : isc.Canvas.RIGHT;
}
return this.Super('getCellAlign', arguments);
},
// only set active view but don't do any context menu
cellContextClick: function () {
this.view.setAsActiveView();
return false;
},
view: this.view,
getCellValue: function (record, recordNum, fieldNum, gridBody) {
var field = this.getField(fieldNum),
gridField, func = this.parentElement.getGridSummaryFunction(field),
value = record && field ? (field.displayField ? record[field.displayField] : record[field.name]) : null;
// get the summary function from the main grid
if (!func) {
delete field.summaryFunction;
} else {
field.summaryFunction = func;
}
// handle count much simpler than smartclient does
// so no extra titles or formatting
if (record && func === 'count' && value >= 0) {
return value;
}
return this.Super('getCellValue', arguments);
}
};
var thisGrid = this,
localEditLinkField;
if (this.editGrid) {
// add the edit pencil in the beginning
localEditLinkField = isc.addProperties({}, this.editLinkFieldProperties);
localEditLinkField.width = this.editLinkColumnWidth;
this.fields.unshift(localEditLinkField);
// is the column after the checkbox field
this.editLinkColNum = 1;
}
this.editFormDefaults = isc.addProperties({}, isc.clone(OB.ViewFormProperties), this.editFormDefaults);
// added for showing counts in the filtereditor row
this.checkboxFieldProperties = isc.addProperties({}, this.checkboxFieldProperties | {}, {
canFilter: true,
// frozen is much nicer, but check out this forum discussion:
// http://forums.smartclient.com/showthread.php?p=57581
frozen: true,
canFreeze: true,
showHover: true,
prompt: OB.I18N.getLabel('OBUIAPP_GridSelectAllColumnPrompt'),
filterEditorType: 'StaticTextItem'
});
var grid = this;
var menuItems = [{
title: OB.I18N.getLabel('OBUIAPP_CreateRecordInGrid'),
click: function () {
grid.deselectAllRecords();
grid.startEditingNew();
}
}, {
title: OB.I18N.getLabel('OBUIAPP_CreateRecordInForm'),
click: function () {
grid.deselectAllRecords();
grid.view.newDocument();
}
}];
if (this.showSortArrow === 'field') {
// solves https://issues.openbravo.com/view.php?id=17362
this.showSortArrow = isc.ListGrid.BOTH;
this.sorterDefaults = {};
}
// TODO: add dynamic part of readonly (via setWindowSettings: see issue 17441)
// add context-menu only if 'new' is allowed in tab definition
if (this.uiPattern !== 'SR' && this.uiPattern !== 'RO' && this.uiPattern !== 'ED') {
this.contextMenu = this.getMenuConstructor().create({
items: menuItems
});
this.contextMenu.show = function () {
var me = this;
// If not in the header tab, and no parent is selected, do not show the context menu
// See issue https://issues.openbravo.com/view.php?id=21787
if (!grid.view.hasValidState()) {
return;
}
if (grid.isGrouped) {
return;
}
if (!grid.view.isActiveView()) {
// The view where the context menu is being opened must be active
// See issue https://issues.openbravo.com/view.php?id=20872
grid.view.setAsActiveView(true);
setTimeout(function () {
me.Super('show', arguments);
}, 10);
} else {
me.Super('show', arguments);
}
};
}
var ret = this.Super('initWidget', arguments);
// only show summary rows if there are summary functions
for (i = 0; i < this.getFields().length; i++) {
if (this.getFields()[i].summaryFunction) {
this.showGridSummary = true;
}
}
// only personalize if there is a professional license
if (!OB.Utilities.checkProfessionalLicense(null, true)) {
vwState = this.view.standardWindow.getDefaultGridViewState(this.view.tabId);
if (vwState) {
this.setViewState(vwState);
}
}
this.noDataEmptyMessage = '' + OB.I18N.getLabel('OBUISC_ListGrid.loadingDataMessage') + ''; // OB.I18N.getLabel('OBUIAPP_GridNoRecords')
this.filterNoRecordsEmptyMessage = '' + OB.I18N.getLabel('OBUIAPP_GridFilterNoResults') + '' + '' + OB.I18N.getLabel('OBUIAPP_GridClearFilter') + '';
return ret;
},
clearFilter: function () {
// hide the messagebar
this.view.messageBar.hide();
this.Super('clearFilter', arguments);
},
// select the first field after the frozen fields
// as the one to use for grouping headers
getGroupTitleField: function () {
var frozenFields = this.frozenFields;
if (frozenFields) {
// first field after frozen section
return this.getField(frozenFields.length).name;
}
// field number 2 is the first one after the standard frozen section
return this.getField(2).name;
},
// prevent a jscript error if there are no group summary functions
getGroupSummaryData: function () {
var ret = this.Super('getGroupSummaryData', arguments);
if (isc.isAn.Array(ret) && !ret[0]) {
return [{}];
}
return ret;
},
// Overridden to sort before grouping, so that groups are sorted
// and open initial group, move the group field to the left,
// or put it back when ungrouping
groupBy: function (fields) {
var fld, currentGroupByFields, currentGroupByField;
// move the current group column to where it came from
currentGroupByFields = this.getGroupByFields();
// no changes go away
if (!currentGroupByFields && !fields) {
return;
} else if (fields === currentGroupByFields) {
return;
}
if (currentGroupByFields && currentGroupByFields[0]) {
currentGroupByField = this.getField(currentGroupByFields[0]);
currentGroupByField.canReorder = true;
currentGroupByField.canHide = true;
this.reorderField(this.getFieldNum(currentGroupByField), currentGroupByField.previousFieldNum);
}
// first sort so that groups are correctly sorted
if (fields) {
if (isc.isAn.Array(fields)) {
fld = fields[0];
} else {
fld = fields;
}
this.getField(fld).previousFieldNum = this.getFieldNum(fld);
fld = this.getField(fld);
fld.canReorder = false;
fld.canHide = false;
this.reorderField(this.getFieldNum(fld), 0);
this.sort(fld);
}
this.Super('groupBy', arguments);
this.view.toolBar.updateButtonState(true);
// when there was already a group open, changing the group by
// starts with all groups closed, explicitly open the first group
if (fields && currentGroupByFields) {
this.openInitialGroups();
}
this.view.standardWindow.storeViewState();
},
clearGroupBy: function () {
var currentGroupByFields, currentGroupByField;
// reason for clearing was large dataset, tell the user
if (this.data && this.data.getLength() > this.groupByMaxRecords) {
// move the current group column to where it came from
currentGroupByFields = this.getGroupByFields();
if (currentGroupByFields && currentGroupByFields[0]) {
currentGroupByField = this.getField(currentGroupByFields[0]);
currentGroupByField.canReorder = true;
currentGroupByField.canHide = true;
this.reorderField(this.getFieldNum(currentGroupByField), currentGroupByField.previousFieldNum);
}
this.Super('clearGroupBy');
this.view.standardWindow.storeViewState();
isc.say(OB.I18N.getLabel('OBUIAPP_MaxGroupingReached', [this.groupByMaxRecords]));
} else {
this.Super('clearGroupBy');
}
},
// Overrides the standard SC function as that function
// also returns the default summary function from the
// type definition. We only want the explicitly set
// summary functions.
getGridSummaryFunction: function (field) {
if (!field) {
return;
}
return field.summaryFunction;
},
// when the summary information changes, refresh
// the grid in the correct way
setSummaryFunctionActions: function (clear) {
var i, noSummaryFunction;
if (this.isGrouped) {
this.regroup();
}
if (!clear) {
if (!this.showGridSummary) {
this.setShowGridSummary(true);
}
this.recalculateGridSummary();
} else if (this.showGridSummary) {
noSummaryFunction = true;
for (i = 0; i < this.getFields().length; i++) {
if (this.getFields()[i].summaryFunction) {
noSummaryFunction = false;
break;
}
}
if (noSummaryFunction) {
this.setShowGridSummary(false);
} else {
this.recalculateGridSummary();
}
}
},
getHeaderContextMenuItems: function (colNum) {
var field = this.getField(colNum),
i, summarySubMenu = [],
grid = this,
groupByFields = this.getGroupByFields(),
type, isDate, isNumber, menuItems = this.Super('getHeaderContextMenuItems', arguments);
// remove the group by menu option if the field is grouped
// and it does not have a submenu
if (groupByFields && groupByFields.contains(field.name)) {
for (i = 0; i < menuItems.length; i++) {
if (menuItems[i].groupItem && !menuItems[i].submenu) {
menuItems.removeAt(i);
break;
}
}
}
if (field) {
type = isc.SimpleType.getType(field.type);
isDate = isc.SimpleType.inheritsFrom(type, 'date');
isNumber = isc.SimpleType.inheritsFrom(type, 'integer') || isc.SimpleType.inheritsFrom(type, 'float');
if (isNumber && !field.clientClass) {
summarySubMenu.add({
title: OB.I18N.getLabel('OBUIAPP_SummaryFunctionSum'),
// enabled: field.summaryFunction != 'sum',
checked: field.summaryFunction === 'sum',
click: function (target, item) {
field.summaryFunction = 'sum';
grid.setSummaryFunctionActions();
}
});
summarySubMenu.add({
title: OB.I18N.getLabel('OBUIAPP_SummaryFunctionAvg'),
// enabled: field.summaryFunction != 'avg',
checked: field.summaryFunction === 'avg',
click: function (target, item) {
field.summaryFunction = 'avg';
grid.setSummaryFunctionActions();
}
});
}
if (!field.clientClass) {
summarySubMenu.add({
title: OB.I18N.getLabel('OBUIAPP_SummaryFunctionMin'),
checked: field.summaryFunction === 'min',
click: function (target, item) {
field.summaryFunction = 'min';
grid.setSummaryFunctionActions();
}
});
summarySubMenu.add({
title: OB.I18N.getLabel('OBUIAPP_SummaryFunctionMax'),
checked: field.summaryFunction === 'max',
click: function (target, item) {
field.summaryFunction = 'max';
grid.setSummaryFunctionActions();
}
});
}
summarySubMenu.add({
title: OB.I18N.getLabel('OBUIAPP_SummaryFunctionCount'),
// enabled: field.summaryFunction != 'count',
checked: field.summaryFunction === 'count',
click: function (target, item) {
field.summaryFunction = 'count';
grid.setSummaryFunctionActions();
}
});
menuItems.add({
isSeparator: true
});
menuItems.add({
groupItem: true,
title: OB.I18N.getLabel('OBUIAPP_SetSummaryFunction'),
fieldName: field.name,
targetField: field,
prompt: OB.I18N.getLabel('OBUIAPP_SetSummaryFunction_Description'),
canSelectParent: true,
submenu: summarySubMenu
});
if (field.summaryFunction) {
menuItems.add({
title: OB.I18N.getLabel('OBUIAPP_ClearSummaryFunction'),
targetField: field,
click: function (target, item) {
delete field.summaryFunction;
grid.setSummaryFunctionActions(true);
}
});
}
menuItems.add({
title: OB.I18N.getLabel('OBUIAPP_ClearSummaries'),
targetField: field,
click: function (target, item) {
var i, fld;
for (i = 0; i < grid.getFields().length; i++) {
fld = grid.getFields()[i];
delete fld.summaryFunction;
}
grid.setSummaryFunctionActions(true);
}
});
}
// add the summary functions
return menuItems;
},
// overridden to load all data in one request
requestVisibleRows: function () {
// fake smartclient to think that there groupByMaxRecords + 1 records
if (this.data && this.isGrouped && !this.data.allRows) {
this.data.totalRows = this.groupByMaxRecords + 1;
}
this.Super('requestVisibleRows', arguments);
},
// Overridden to make sure that the group header is not shown in
// the frozen body
getGroupNodeHTML: function (node, gridBody) {
var isFrozenBody = this.frozenBody === gridBody;
if (this.frozenBody && isFrozenBody) {
return this.emptyCellValue;
}
var state = this.data.isOpen(node) ? 'opened' : 'closed',
url = isc.Img.urlForState(this.groupIcon, null, null, state),
iconIndent = isc.Canvas.spacerHTML(this.groupIconPadding, 1),
groupIndent = isc.Canvas.spacerHTML((this.data.getLevel(node) - 1) * this.groupIndentSize + this.groupLeadingIndent, 1);
var img = this.imgHTML(url, this.groupIconSize, this.groupIconSize);
var retStr = (this.canCollapseGroup ? groupIndent + img + iconIndent + this.getGroupTitle(node) : groupIndent + iconIndent + this.getGroupTitle(node));
return retStr;
},
filterEditorSubmit: function () {
// hide the messagebar
this.view.messageBar.hide();
this.Super('filterEditorSubmit', arguments);
},
// destroy the context menu also
// see why this needs to be done in the
// documentation of canvas.contextMenu in Canvas.js
destroy: function () {
var i, field, fields = this.getFields(),
editorProperties, len = fields.length,
ds, dataSources = [];
if (this.getDataSource()) {
// will get destroyed in the super class then
this.getDataSource().potentiallyShared = false;
}
for (i = 0; i < len; i++) {
field = fields[i];
editorProperties = field && field.editorProperties;
ds = editorProperties && editorProperties.optionDataSource;
if (ds) {
dataSources.push(ds);
}
}
if (this.contextMenu) {
this.contextMenu.destroy();
this.contextMenu = null;
}
this.Super('destroy', arguments);
len = dataSources.length;
for (i = 0; i < len; i++) {
ds = dataSources[i];
if (ds) {
ds.destroy();
ds = null;
}
}
},
setData: function (data) {
data.grid = this;
this.Super('setData', arguments);
},
refreshFields: function () {
this.setFields(this.completeFields.duplicate());
},
setReadOnlyMode: function () {
if (this.uiPattern !== 'RO') {
this.uiPattern = 'RO';
this.canEdit = false;
if (this.contextMenu) {
this.contextMenu.destroy();
this.contextMenu = null;
}
this.refreshContents();
}
},
draw: function () {
var drawnBefore = this.isDrawn(),
i, form, item, items, length;
this.enableShortcuts();
this.Super('draw', arguments);
// set the focus in the filter editor
if (this.view && this.view.isActiveView() && !drawnBefore && this.isVisible() && this.getFilterEditor() && this.getFilterEditor().getEditForm()) {
// there is a filter editor
form = this.getFilterEditor().getEditForm();
// compute a focus item, set focus with some delay
// to give everyone time to be ready
if (!form.getFocusItem()) {
items = form.getItems();
length = items.length;
for (i = 0; i < length; i++) {
item = items[i];
if (item.getCanFocus() && !item.isDisabled()) {
item.delayCall('focusInItem', null, 100);
break;
}
}
} else {
form.getFocusItem().delayCall('focusInItem', null, 100);
}
}
},
// add the properties from the form
addFormProperties: function (props) {
isc.addProperties(this.editFormDefaults, props);
},
getCellVAlign: function () {
return 'center';
},
getCellAlign: function (record, rowNum, colNum) {
var fld = this.getFields()[colNum],
isRTL = this.isRTL(),
func = this.getGridSummaryFunction(fld),
isSummary = record && (record[this.groupSummaryRecordProperty] || record[this.gridSummaryRecordProperty]);
if (!fld.clientClass && rowNum === this.getEditRow()) {
return 'center';
}
if (isSummary && func === 'count') {
return isRTL ? isc.Canvas.LEFT : isc.Canvas.RIGHT;
}
return this.Super('getCellAlign', arguments);
},
// overridden to support hover on the header for the checkbox field
setFieldProperties: function (field, properties) {
var localField = field;
if (isc.isA.Number(localField)) {
localField = this.fields[localField];
}
if (this.isCheckboxField(localField) && properties) {
properties.showHover = true;
properties.prompt = OB.I18N.getLabel('OBUIAPP_GridSelectAllColumnPrompt');
}
return this.Super('setFieldProperties', arguments);
},
cellHoverHTML: function (record, rowNum, colNum) {
var ret, field = this.getField(colNum),
cellErrors, msg = '',
prefix = '',
i, func = this.getGridSummaryFunction(field),
isGroupOrSummary = record && (record[this.groupSummaryRecordProperty] || record[this.gridSummaryRecordProperty]);
if (!record) {
return;
}
if (func && (isGroupOrSummary)) {
if (func === 'sum') {
prefix = OB.I18N.getLabel('OBUIAPP_SummaryFunctionSum');
}
if (func === 'min') {
prefix = OB.I18N.getLabel('OBUIAPP_SummaryFunctionMin');
}
if (func === 'max') {
prefix = OB.I18N.getLabel('OBUIAPP_SummaryFunctionMax');
}
if (func === 'count') {
prefix = OB.I18N.getLabel('OBUIAPP_SummaryFunctionCount');
}
if (func === 'avg') {
prefix = OB.I18N.getLabel('OBUIAPP_SummaryFunctionAvg');
}
if (prefix) {
prefix = prefix + ' ';
}
}
if (this.isCheckboxField(field)) {
return OB.I18N.getLabel('OBUIAPP_GridSelectColumnPrompt');
}
if (this.cellHasErrors(rowNum, colNum)) {
cellErrors = this.getCellErrors(rowNum, colNum);
// note cellErrors can be a string or array
// accidentally both have the length property
if (cellErrors && cellErrors.length > 0) {
return OB.Utilities.getPromptString(cellErrors);
}
}
if (record && record[isc.OBViewGrid.ERROR_MESSAGE_PROP]) {
return record[isc.OBViewGrid.ERROR_MESSAGE_PROP];
}
this.inCellHoverHTML = true;
ret = this.Super('cellHoverHTML', arguments);
delete this.inCellHoverHTML;
return prefix + (ret ? ret : '');
},
reorderField: function (fieldNum, moveToPosition) {
var res = this.Super('reorderField', arguments);
this.view.standardWindow.storeViewState();
return res;
},
hideField: function (field, suppressRelayout) {
var res;
this._hidingField = true;
this._savedEditValues = this.getEditValues(this.getEditRow());
res = this.Super('hideField', arguments);
delete this._savedEditValues;
delete this._hidingField;
this.view.standardWindow.storeViewState();
this.refreshContents();
return res;
},
showField: function (field, suppressRelayout) {
var res;
this._showingField = true;
this._savedEditValues = this.getEditValues(this.getEditRow());
res = this.Super('showField', arguments);
delete this._savedEditValues;
delete this._showingField;
this.view.standardWindow.storeViewState();
this.refreshContents();
return res;
},
resizeField: function (fieldNum, newWidth, storeWidth) {
var res = this.Super('resizeField', arguments);
this.view.standardWindow.storeViewState();
return res;
},
// also store the filter criteria
getViewState: function (returnObject, includeFilter) {
var i, fld, state = this.Super('getViewState', [returnObject || true]);
if (includeFilter) {
state.filter = this.getCriteria();
if (!this.filterClause) {
state.noFilterClause = true;
}
}
// set summary information, can not be stored in the field state
// because smartclient does not provide a nice override point
// when setting the fieldstate back to also set the summary function
state.summaryFunctions = {};
for (i = 0; i < this.getAllFields().length; i++) {
fld = this.getAllFields()[i];
if (fld.summaryFunction && isc.isA.String(fld.summaryFunction)) {
state.summaryFunctions[fld.name] = fld.summaryFunction;
}
}
// get rid of the selected state
delete state.selected;
this.deleteSelectedParentRecordFilter(state);
if (returnObject) {
return state;
}
return '(' + isc.Comm.serialize(state, false) + ')';
},
setViewState: function (state) {
var localState, i, fld, hasSummaryFunction;
localState = this.evalViewState(state, 'viewState');
// strange case, sometimes need to call twice
if (isc.isA.String(localState)) {
localState = this.evalViewState(state, 'viewState');
}
if (!localState) {
return;
}
if (this.getDataSource()) {
// old versions stored selected records in grid view, this can cause
// problems if record is not selected yet
delete localState.selected;
this.deselectAllRecords();
if (localState.summaryFunctions) {
hasSummaryFunction = false;
for (i = 0; i < this.getAllFields().length; i++) {
fld = this.getAllFields()[i];
if (localState.summaryFunctions[fld.name]) {
hasSummaryFunction = true;
fld.summaryFunction = localState.summaryFunctions[fld.name];
} else {
delete fld.summaryFunction;
}
}
this.setShowGridSummary(hasSummaryFunction);
}
// remove focus as this results in blur behavior before the
// (filter)editor is redrawn with new fields when
// doing setviewstate
// https://issues.openbravo.com/view.php?id=21249
if (this.getEditForm() && this.getEditForm().getFocusItem()) {
this.getEditForm().getFocusItem().hasFocus = false;
}
if (this.filterEditor && this.filterEditor.getEditForm() && this.filterEditor.getEditForm().getFocusItem()) {
this.filterEditor.getEditForm().getFocusItem().hasFocus = false;
}
this.deleteSelectedParentRecordFilter(localState);
this.Super('setViewState', ['(' + isc.Comm.serialize(localState, false) + ')']);
// Focus on the first filterable item
if (this.view.isActiveView()) {
this.focusInFirstFilterEditor();
}
}
if (localState.noFilterClause) {
this.filterClause = null;
if (this.view.messageBar) {
this.view.messageBar.hide();
}
}
// and no additional filter clauses passed in
if (localState.filter && this.view.tabId !== this.view.standardWindow.additionalCriteriaTabId && this.view.tabId !== this.view.standardWindow.additionalFilterTabId) {
// a filtereditor but no editor yet
// set it in the initialcriteria of the filterEditro
if (this.filterEditor && !this.filterEditor.getEditForm()) {
this.filterEditor.setValuesAsCriteria(localState.filter);
}
this.setCriteria(localState.filter);
}
},
// overridden to also store the group mode
// http://forums.smartclient.com/showthread.php?p=93877#post93877
getGroupState: function () {
var i, fld, state = this.Super('getGroupState', arguments),
result = {};
result.groupByFields = state;
result.groupingModes = {};
for (i = 0; i < this.getFields().length; i++) {
fld = this.getFields()[i];
if (fld.groupingMode) {
result.groupingModes[fld.name] = fld.groupingMode;
}
}
return result;
},
setGroupState: function (state) {
var i, fld, key;
if (state && (state.groupByFields || state.groupByFields === '')) {
if (state.groupingModes) {
for (key in state.groupingModes) {
if (state.groupingModes.hasOwnProperty(key)) {
fld = this.getField(key);
if (fld) {
fld.groupingMode = state.groupingModes[key];
}
}
}
}
this.Super('setGroupState', [state.groupByFields]);
} else {
// older state definition
this.Super('setGroupState', arguments);
}
},
// Deletes the implicit filter on the selected record of the parent
deleteSelectedParentRecordFilter: function (state) {
var i, filterLength, filterItem;
if (state.filter) {
filterLength = state.filter.criteria.length;
for (i = 0; i < filterLength; i++) {
filterItem = state.filter.criteria[i];
if (filterItem.fieldName === this.view.parentProperty) {
// This way it is ensured that the sub tabs will not show the registers associated with
// the register of its parent tab that was selected when the filter was created
state.filter.criteria[i].value = '-1';
break;
}
}
}
},
getSummaryRowDataSource: function () {
if (this.getSummarySettings()) {
return this.getDataSource();
}
},
getSummaryRowFetchRequestConfig: function () {
var fld, i, summary = this.getSummarySettings(),
config = this.Super('getSummaryRowFetchRequestConfig', arguments);
if (summary) {
config.params = config.params || {};
config.params._summary = summary;
config.params = this.getFetchRequestParams(config.params);
}
return config;
},
getSummarySettings: function () {
var fld, i, summary;
for (i = 0; i < this.getFields().length; i++) {
fld = this.getFields()[i];
if (fld.summaryFunction && isc.OBViewGrid.SUPPORTED_SUMMARY_FUNCTIONS.contains(fld.summaryFunction)) {
summary = summary || {};
summary[fld.displayField || fld.name] = fld.summaryFunction;
}
}
return summary;
},
setView: function (view) {
var dataPageSizeaux, length, i, crit, groupByMaxRecords;
this.view = view;
this.editFormDefaults.view = view;
if (this.getField(this.view.parentProperty)) {
this.getField(this.view.parentProperty).canFilter = false;
this.getField(this.view.parentProperty).canEdit = false;
}
// Begins-added to have the additional filter clause and tabid..Mallikarjun M
// URL example:http://localhost:8080/openbravo/?tabId=186&filterClause=e.businessPartner.searchKey%3D%27mcgiver%27&replaceDefaultFilter=true&
if (this.view.tabId === this.view.standardWindow.additionalFilterTabId) {
if (!this.filterClause || this.view.standardWindow.replaceDefaultFilter === 'true') {
this.filterClause = unescape(this.view.standardWindow.additionalFilterClause);
} else if (this.filterClause) {
this.filterClause = '((' + this.filterClause + ') and (' + unescape(this.view.standardWindow.additionalFilterClause) + '))';
}
}
// Ends..
if (this.view.tabId === this.view.standardWindow.additionalCriteriaTabId && this.view.standardWindow.additionalCriteria) {
crit = isc.JSON.decode(unescape(this.view.standardWindow.additionalCriteria));
this.setCriteria(crit);
delete this.view.standardWindow.additionalCriteria;
}
// if there is no autoexpand field then just divide the space
if (!this.getAutoFitExpandField()) {
length = this.fields.length;
// nobody, then give all the fields a new size, dividing
// the space among them
for (i = 0; i < length; i++) {
// ignore the first 2 fields, the checkbox and edit/form
// buttons
if (i > 1) {
this.fields[i].width = '*';
}
}
}
// Modify the quantity of lines to count per Window
dataPageSizeaux = OB.PropertyStore.get('dataPageSize', this.view.standardWindow.windowId);
this.dataPageSize = dataPageSizeaux ? +dataPageSizeaux : 100;
groupByMaxRecords = OB.PropertyStore.get('OBUIAPP_GroupingMaxRecords', this.view.standardWindow.windowId);
this.groupByMaxRecords = groupByMaxRecords ? +groupByMaxRecords : 1000;
this.canGroupBy = 'Y' === OB.PropertyStore.get('OBUIAPP_GroupingEnabled', this.view.standardWindow.windowId);
},
show: function () {
var ret = this.Super('show', arguments);
this.view.toolBar.updateButtonState(true);
this.updateRowCountDisplay();
this.resetEmptyMessage();
return ret;
},
headerClick: function (fieldNum, header, autoSaveDone) {
delete this.wasEditing;
if (!autoSaveDone) {
var actionObject = {
target: this,
method: this.headerClick,
parameters: [fieldNum, header, true]
};
this.view.standardWindow.doActionAfterAutoSave(actionObject, true);
return;
}
var field = this.fields[fieldNum];
if (this.isCheckboxField(field) && this.singleRecordSelection) {
this.deselectAllRecords();
this.singleRecordSelection = false;
}
return this.Super('headerClick', arguments);
},
keyPress: function () {
var response = OB.KeyboardManager.Shortcuts.monitor('OBViewGrid');
if (response !== false) {
response = this.Super('keyPress', arguments);
}
return response;
},
bodyKeyPress: function (event, eventInfo) {
var response = OB.KeyboardManager.Shortcuts.monitor('OBViewGrid.body');
if (response !== false) {
if (event && event.keyName === 'Space' && (isc.EventHandler.ctrlKeyDown() || isc.EventHandler.altKeyDown() || isc.EventHandler.shiftKeyDown())) {
return true;
}
response = this.Super('bodyKeyPress', arguments);
}
return response;
},
editFormKeyDown: function () {
// Custom method. Only works if the form is an OBViewForm
var response = OB.KeyboardManager.Shortcuts.monitor('OBViewGrid.editForm');
if (response !== false) {
response = this.Super('editFormKeyDown', arguments);
}
return response;
},
// called when the view gets activated
setActive: function (active) {
if (active) {
this.enableShortcuts();
} else {
this.disableShortcuts();
}
},
disableShortcuts: function () {
OB.KeyboardManager.Shortcuts.set('ViewGrid_EditInGrid', null, function () {
return true;
});
OB.KeyboardManager.Shortcuts.set('ViewGrid_EditInForm', null, function () {
return true;
});
},
enableShortcuts: function () {
var me = this,
ksAction_CancelEditing, ksAction_MoveUpWhileEditing, ksAction_MoveDownWhileEditing, ksAction_DeleteSelectedRecords, ksAction_EditInGrid, ksAction_EditInForm, ksAction_CancelChanges;
// This is JUST for the case of an editing row with the whole row in "read only mode"
ksAction_MoveUpWhileEditing = function () {
if (me.getEditForm()) {
var editRow = me.getEditRow();
me.cancelEditing();
if (editRow) {
me.startEditing(editRow - 1);
}
return false; // To avoid keyboard shortcut propagation
} else {
return true;
}
};
OB.KeyboardManager.Shortcuts.set('ViewGrid_MoveUpWhileEditing', 'OBViewGrid.body', ksAction_MoveUpWhileEditing, null, {
"key": "Arrow_Up"
});
// This is JUST for the case of an editing row with the whole row in "read only mode"
ksAction_MoveDownWhileEditing = function () {
if (me.getEditForm()) {
var editRow = me.getEditRow();
me.cancelEditing();
if (editRow || editRow === 0) {
me.startEditing(editRow + 1);
}
return false; // To avoid keyboard shortcut propagation
} else {
return true;
}
};
OB.KeyboardManager.Shortcuts.set('ViewGrid_MoveDownWhileEditing', 'OBViewGrid.body', ksAction_MoveDownWhileEditing, null, {
"key": "Arrow_Down"
});
ksAction_CancelEditing = function () {
if (me.getEditForm()) {
me.cancelEditing();
return false; // To avoid keyboard shortcut propagation
} else {
return true;
}
};
OB.KeyboardManager.Shortcuts.set('ViewGrid_CancelEditing', ['OBViewGrid.body', 'OBViewGrid.editForm'], ksAction_CancelEditing);
ksAction_DeleteSelectedRecords = function () {
var isRecordDeleted = me.deleteSelectedRowsByToolbarIcon();
if (isRecordDeleted) {
return false; // To avoid keyboard shortcut propagation
} else {
return true;
}
};
OB.KeyboardManager.Shortcuts.set('ViewGrid_DeleteSelectedRecords', 'OBViewGrid.body', ksAction_DeleteSelectedRecords);
ksAction_EditInGrid = function () {
if (me.getSelectedRecords().length === 1) {
me.endEditing();
me.startEditing(me.getRecordIndex(me.getSelectedRecords()[0]));
return false; // To avoid keyboard shortcut propagation
} else {
return true;
}
};
OB.KeyboardManager.Shortcuts.set('ViewGrid_EditInGrid', 'OBViewGrid.body', ksAction_EditInGrid);
ksAction_EditInForm = function () {
if (me.getSelectedRecords().length === 1) {
me.endEditing();
me.view.editRecord(me.getSelectedRecords()[0]);
return false; // To avoid keyboard shortcut propagation
} else {
return true;
}
};
OB.KeyboardManager.Shortcuts.set('ViewGrid_EditInForm', ['OBViewGrid.body', 'OBViewGrid.editForm'], ksAction_EditInForm);
this.Super('enableShortcuts', arguments);
},
deselectAllRecords: function (preventUpdateSelectInfo, autoSaveDone) {
// if there is nothing to deselect then don't deselect
if (!this.getSelectedRecord()) {
return;
}
if (!autoSaveDone) {
var actionObject = {
target: this,
method: this.deselectAllRecords,
parameters: [preventUpdateSelectInfo, true]
};
this.view.standardWindow.doActionAfterAutoSave(actionObject, true);
return;
}
this.allSelected = false;
var ret = this.Super('deselectAllRecords', arguments);
this.lastSelectedRecord = null;
if (!preventUpdateSelectInfo) {
this.selectionUpdated();
}
return ret;
},
selectAllRecords: function (autoSaveDone) {
if (!autoSaveDone) {
var actionObject = {
target: this,
method: this.selectAllRecords,
parameters: [true]
};
this.view.standardWindow.doActionAfterAutoSave(actionObject, true);
return;
}
this.allSelected = true;
var ret = this.Super('selectAllRecords', arguments);
this.selectionUpdated();
return ret;
},
updateRowCountDisplay: function (delayed) {
if (!delayed) {
this.delayCall('updateRowCountDisplay', [true], 100);
return;
}
var newValue = '',
length = isc.isA.Tree(this.data) ? this.countGroupContent() : this.data.getLength();
if (length > this.dataPageSize) {
newValue = '>' + this.dataPageSize;
} else if (length === 0) {
newValue = ' ';
} else {
newValue = length;
}
if (this.filterEditor && this.filterEditor.getEditForm()) {
this.filterEditor.getEditForm().setValue(isc.OBViewGrid.EDIT_LINK_FIELD_NAME, newValue);
this.filterEditor.getEditForm().getField(isc.OBViewGrid.EDIT_LINK_FIELD_NAME).defaultValue = newValue;
}
},
countGroupContent: function () {
var i, cnt = 0,
data = this.data.getRange(0, this.groupByMaxRecords + 1);
for (i = 0; i < data.length; i++) {
if (!data[i].isFolder) {
cnt++;
}
}
return cnt;
},
refreshContents: function (callback) {
var selectedValues, context;
this.resetEmptyMessage();
this.view.updateTabTitle();
delete this.initialCriteria;
// do not refresh if the parent is not selected and we have no data
// anyway
if (this.view.parentProperty && (!this.data || !this.data.getLength || this.data.getLength() === 0)) {
selectedValues = this.view.parentView.viewGrid.getSelectedRecords();
if (selectedValues && !this.isOpenDirectMode && selectedValues.length === 0) {
if (callback) {
callback();
}
// but in this case we should show ourselves also
if (!this.isVisible()) {
this.makeVisible();
}
return;
}
}
context = {
showPrompt: false
};
this.filterData(this.getCriteria(), callback, context);
},
// the dataarrived method is where different actions are done after
// data has arrived in the grid:
// - open the edit view if default edit mode is enabled
// - if the user goes directly to a tab (from a link in another window)
// then
// opening the relevant record is done here or if no record is passed grid
// mode is opened
// - if there is only one record then select it directly
dataArrived: function (startRow, endRow) {
var noSetSession, changeEvent, forceUpdate;
// do this now, to replace the loading message
// TODO: add dynamic part of readonly (via setWindowSettings: see issue 17441)
if (this.uiPattern === 'SR' || this.uiPattern === 'RO' || this.uiPattern === 'ED') {
this.noDataEmptyMessage = '' + OB.I18N.getLabel('OBUIAPP_NoDataInGrid') + '';
} else {
this.noDataEmptyMessage = '' + OB.I18N.getLabel('OBUIAPP_GridNoRecords') + '' + '' + OB.I18N.getLabel('OBUIAPP_GridCreateOne') + '';
}
this.resetEmptyMessage();
var record, ret = this.Super('dataArrived', arguments);
this.updateRowCountDisplay();
// TODO: Clear Row Count before loading new data
var newValue = ' ';
this.filterEditor.getEditForm().setValue(isc.OBViewGrid.EDIT_LINK_FIELD_NAME, newValue);
if (this.getSelectedRecords() && this.getSelectedRecords().length > 0) {
this.selectionUpdated();
}
// no data and the grid is not visible, only do this is if the
// form is not in new mode
if (this.data && this.data.getLength() === 0 && !this.isVisible() && !this.view.viewForm.isNew) {
this.makeVisible();
}
// get the record id from any record
if (this.isOpenDirectMode && this.data && this.data.getLength() >= 1) {
// now tell the parent grid to refresh on the basis of this parentRecordId also
if (this.view.parentView) {
this.view.parentRecordId = this.data.get(startRow)[this.view.parentProperty];
this.view.parentView.viewGrid.isOpenDirectMode = true;
// makes sure that the parent refresh will not fire back to cause a child refresh
this.view.parentView.isOpenDirectModeParent = true;
// prevents opening edit mode for parent views
this.view.parentView.viewGrid.isOpenDirectModeParent = true;
this.view.parentView.viewGrid.targetRecordId = this.view.parentRecordId;
this.view.parentView.viewGrid.delayCall('refreshContents', [], 10);
}
}
delete this.isOpenDirectMode;
if (!this.targetRecordId) {
delete this.isOpenDirectModeLeaf;
}
if (this.targetOpenNewEdit) {
delete this.targetOpenNewEdit;
// not passing record opens new
this.view.editRecord();
} else if (this.targetOpenGrid) {
// direct link from other window but without a record id
// so just show grid mode
// don't need to do anything here
delete this.targetOpenGrid;
} else if (this.targetRecordId) {
// direct link from other tab to a specific record
this.delayedHandleTargetRecord(startRow, endRow);
} else if (this.view.shouldOpenDefaultEditMode()) {
// ui-pattern: single record/edit mode
this.view.openDefaultEditView(this.getRecord(startRow));
} else if (this.data && this.data.getLength() === 1) {
// one record select it directly
record = this.getRecord(0);
// this select method prevents state changing if the record
// was already selected
this.doSelectSingleRecord(record);
// Call to updateButtonState to force a call to the FIC in setsession mode
// See issue https://issues.openbravo.com/view.php?id=22655
noSetSession = false;
changeEvent = false;
forceUpdate = true;
this.view.toolBar.updateButtonState(noSetSession, changeEvent, forceUpdate);
} else if (this.lastSelectedRecord) {
// if nothing was select, select the record again
if (!this.getSelectedRecord()) {
// if it is still in the cache ofcourse
var gridRecord = this.data.find(OB.Constants.ID, this.lastSelectedRecord.id);
if (gridRecord) {
this.doSelectSingleRecord(gridRecord);
}
} else if (this.getSelectedRecords() && this.getSelectedRecords().length !== 1) {
this.lastSelectedRecord = null;
}
}
if (this.actionAfterDataArrived) {
this.actionAfterDataArrived();
this.actionAfterDataArrived = null;
}
return ret;
},
removeOrClause: function (criteria) {
// The original criteria is stored in the position #0
// The criteria to select the recently created records is stored in position #1..length-1
return criteria.criteria.get(0);
},
refreshGrid: function (callback, newRecordsToBeIncluded) {
var originalCriteria, criteria = {},
newRecordsCriteria, newRecordsLength, i;
if (this.getSelectedRecord()) {
this.targetRecordId = this.getSelectedRecord()[OB.Constants.ID];
// as the record is already selected it is already in the filter
this.notRemoveFilter = true;
}
this.actionAfterDataArrived = callback;
this.invalidateCache();
var context = {
showPrompt: false
};
// Removes the 'or' clause, if there is one
// See note at the function foot
originalCriteria = this.getCriteria();
if (this._criteriaWithOrClause) {
originalCriteria = this.removeOrClause(originalCriteria);
this._criteriaWithOrClause = false;
}
// If a record has to be included in the refresh, it must be included
// in the filter with an 'or' operator, along with the original filter,
// but only if there is an original filter
if (newRecordsToBeIncluded && newRecordsToBeIncluded.length > 0 && originalCriteria.criteria.length > 0) {
// Adds the current record to the criteria
newRecordsCriteria = [];
newRecordsLength = newRecordsToBeIncluded.length;
for (i = 0; i < newRecordsLength; i++) {
newRecordsCriteria.push({
fieldName: 'id',
operator: 'equals',
value: newRecordsToBeIncluded[i]
});
}
this._criteriaWithOrClause = true;
criteria._constructor = 'AdvancedCriteria';
criteria._OrExpression = true; // trick to get a really _or_ in the backend
criteria.operator = 'or';
criteria.criteria = [originalCriteria].concat(newRecordsCriteria);
} else {
criteria = originalCriteria;
}
this.filterData(criteria, null, context);
// At this point the original criteria should be restored, to prevent
// the 'or' clause that was just added to be used in subsequent refreshes.
// It is not possible to do it here, though, because a this.setCriteria(originalCriteria)
// would trigger an automatic refresh that would leave without effect that last filterData
// The additional criteria will be removed in the next call to refreshGrid
},
// with a delay to handle the target record when the body has been drawn
delayedHandleTargetRecord: function (startRow, endRow) {
var rowTop, recordIndex, i, data = this.data,
tmpTargetRecordId = this.targetRecordId;
if (!this.targetRecordId) {
delete this.isOpenDirectModeLeaf;
return;
}
if (this.body) {
// don't need it anymore
delete this.targetRecordId;
delete this.notRemoveFilter;
var gridRecord = data.find(OB.Constants.ID, tmpTargetRecordId);
// no grid record found, stop here
if (!gridRecord) {
return;
}
recordIndex = this.getRecordIndex(gridRecord);
if (data.criteria) {
data.criteria._targetRecordId = null;
}
this.doSelectSingleRecord(gridRecord);
this.scrollCellIntoView(recordIndex, null, true, true);
// show the form with the selected record
if (!this.view.isShowingForm && this.isOpenDirectModeLeaf) {
this.view.editRecord(gridRecord);
}
delete this.isOpenDirectModeLeaf;
delete this.isOpenDirectModeParent;
} else {
// wait a bit longer til the body is drawn
this.delayCall('delayedHandleTargetRecord', [startRow, endRow], 200, this);
}
},
selectRecordById: function (id, forceFetch) {
if (forceFetch) {
this.targetRecordId = id;
this.filterData(this.getCriteria());
return;
}
var recordIndex, gridRecord = this.data.find(OB.Constants.ID, id);
// no grid record fetch it
if (!gridRecord) {
this.targetRecordId = id;
this.filterData(this.getCriteria());
return;
}
recordIndex = this.getRecordIndex(gridRecord);
this.scrollRecordIntoView(recordIndex, true);
this.doSelectSingleRecord(gridRecord);
},
filterData: function (criteria, callback, requestProperties) {
var theView = this.view,
newCallBack;
if (!requestProperties) {
requestProperties = {};
}
requestProperties.showPrompt = false;
requestProperties.filtering = true;
newCallBack = function () {
theView.recordSelected();
if (callback) {
callback();
}
};
return this.Super('filterData', [this.convertCriteria(criteria), newCallBack, requestProperties]);
},
fetchData: function (criteria, callback, requestProperties) {
var theView = this.view,
newCallBack;
if (!requestProperties) {
requestProperties = {};
}
requestProperties.showPrompt = false;
newCallBack = function () {
theView.recordSelected();
if (callback) {
callback();
}
};
return this.Super('fetchData', [this.convertCriteria(criteria), newCallBack, requestProperties]);
},
handleFilterEditorSubmit: function (criteria, context, autoSaveDone) {
if (!autoSaveDone) {
var actionObject = {
target: this,
method: this.handleFilterEditorSubmit,
parameters: [criteria, context, true]
};
this.view.standardWindow.doActionAfterAutoSave(actionObject, true);
return;
}
this.Super('handleFilterEditorSubmit', arguments);
},
getInitialCriteria: function () {
var criteria = this.Super('getInitialCriteria', arguments);
return this.convertCriteria(criteria);
},
getCriteria: function () {
var criteria = this.Super('getCriteria', arguments) || {};
if ((criteria === null || !criteria.criteria) && this.initialCriteria) {
criteria = isc.shallowClone(this.initialCriteria);
}
criteria = this.convertCriteria(criteria);
return criteria;
},
convertCriteria: function (criteria) {
var selectedValues, prop, fld, value, i, j, k, criterion, fldName, length, today = new Date(),
currentTimeZoneOffsetInMinutes = -today.getTimezoneOffset();
if (!criteria) {
criteria = {};
} else {
criteria = isc.clone(criteria);
}
if (!criteria.operator) {
criteria.operator = 'and';
}
if (!criteria._constructor) {
criteria._constructor = "AdvancedCriteria";
}
if (!criteria.criteria) {
criteria.criteria = [];
}
if (!this.notRemoveFilter && this.targetRecordId) {
// do not filter on anything with a targetrecord
criteria = {
operator: 'and',
_constructor: "AdvancedCriteria",
criteria: []
};
// add a dummy criteria to force a fetch
criteria.criteria.push(isc.OBRestDataSource.getDummyCriterion());
} else if (this.forceRefresh) {
// add a dummy criteria to force a fetch
criteria.criteria.push(isc.OBRestDataSource.getDummyCriterion());
delete this.forceRefresh;
} else {
// remove the _dummy
length = criteria.criteria.length;
for (i = 0; i < length; i++) {
if (criteria.criteria[i].fieldName === isc.OBRestDataSource.DUMMY_CRITERION_NAME) {
criteria.criteria.removeAt(i);
break;
}
}
}
// note pass in criteria otherwise infinite looping!
this.resetEmptyMessage(criteria);
if (this.view.parentProperty && !this.isOpenDirectMode) {
selectedValues = this.view.parentView.viewGrid.getSelectedRecords();
var parentPropertyFilterValue = -1;
if (selectedValues) {
if (selectedValues.length === 0) {
parentPropertyFilterValue = '-1';
} else if (selectedValues.length > 1) {
parentPropertyFilterValue = '-1';
} else {
parentPropertyFilterValue = selectedValues[0][OB.Constants.ID];
}
}
this.view.parentRecordId = parentPropertyFilterValue;
var fnd = false;
var innerCriteria = criteria.criteria;
length = innerCriteria.length;
for (i = 0; i < length; i++) {
criterion = innerCriteria[i];
fldName = criterion.fieldName;
if (fldName === this.view.parentProperty) {
fnd = true;
criterion.operator = 'equals';
criterion.value = parentPropertyFilterValue;
break;
}
}
if (!fnd) {
innerCriteria.add({
fieldName: this.view.parentProperty,
operator: 'equals',
value: parentPropertyFilterValue
});
}
}
// Iterates all the criterias
// -If they are not needed, they are removed
// -Otherwise, if it is a datetime criteria, the UTC offset in minutes is added
if (criteria && criteria.criteria) {
var internalCriteria = criteria.criteria;
for (i = (internalCriteria.length - 1); i >= 0; i--) {
var shouldRemove = false;
criterion = internalCriteria[i];
// but do not remove dummy criterion
if (criterion.fieldName && criterion.fieldName.startsWith('_') && criterion.fieldName !== isc.OBRestDataSource.DUMMY_CRITERION_NAME) {
shouldRemove = true;
} else if (isc.isA.emptyString(criterion.value)) {
shouldRemove = true;
}
if (shouldRemove) {
internalCriteria.removeAt(i);
} else {
var fieldName;
// The first name a date time field is filtered, the fieldName is stored in criteria.criteria[i].criteria[0].fieldName
if (criteria.criteria[i].criteria && criteria.criteria[i].criteria[0]) {
fieldName = criteria.criteria[i].criteria[0].fieldName;
} else { // After the first time, the fieldName is stored in criteria.criteria[i].fieldName
fieldName = criteria.criteria[i].fieldName;
}
for (j = 0; j < this.fields.length; j++) {
if (this.fields[j].name === fieldName && isc.SimpleType.getType(this.fields[j].type).inheritsFrom === "datetime") {
if (criteria.criteria[i].criteria) {
for (k = 0; k < criteria.criteria[i].criteria.length; k++) {
criteria.criteria[i].criteria[k].minutesTimezoneOffset = currentTimeZoneOffsetInMinutes;
}
} else {
criteria.criteria[i].minutesTimezoneOffset = currentTimeZoneOffsetInMinutes;
}
break;
}
}
}
}
}
if (this.view.parentView && !this.view.parentProperty) {
// subtabs without an explicit reference to their parent property
// result in an empty criteria which is ignored not generating the
// request. Forcing load
// See issue #22645
selectedValues = this.view.parentView.viewGrid.getSelectedRecords();
if (selectedValues.length !== 1) {
// if there is not a single record selected, always false criterion
criteria.criteria.push({
fieldName: 'id',
operator: 'equals',
value: '-1'
});
} else {
// with a single record selected, dummy criterion
criteria.criteria.push(isc.OBRestDataSource.getDummyCriterion());
}
}
this.checkShowFilterFunnelIcon(criteria);
return criteria;
},
onFetchData: function (criteria, requestProperties) {
requestProperties = requestProperties || {};
requestProperties.params = this.getFetchRequestParams(requestProperties.params);
},
getFetchRequestParams: function (params) {
params = params || {};
if (this.targetRecordId) {
params._targetRecordId = this.targetRecordId;
if (!this.notRemoveFilter) {
// remove the filter clause we don't want to use it anymore
this.filterClause = null;
}
// this mode means that no parent is selected but the parent needs to be
// determined from the target record and the parent property
if (this.isOpenDirectMode && this.view.parentView) {
params._filterByParentProperty = this.view.parentProperty;
}
if (this.view && this.view.directNavigation) {
params._directNavigation = true;
}
} else if (params._targetRecordId) {
delete params._targetRecordId;
}
// prevent the count operation
params[isc.OBViewGrid.NO_COUNT_PARAMETER] = 'true';
if (this.orderByClause) {
params[OB.Constants.ORDERBY_PARAMETER] = this.orderByClause;
}
// add all the new session properties context info to the requestProperties
isc.addProperties(params, this.view.getContextInfo(true, false));
if (this.filterClause) {
if (this.whereClause) {
params[OB.Constants.WHERE_PARAMETER] = ' ((' + this.whereClause + ') and (' + this.filterClause + ")) ";
} else {
params[OB.Constants.WHERE_PARAMETER] = this.filterClause;
}
} else if (this.whereClause) {
params[OB.Constants.WHERE_PARAMETER] = this.whereClause;
} else {
params[OB.Constants.WHERE_PARAMETER] = null;
}
return params;
},
createNew: function () {
this.view.editRecord();
},
makeVisible: function () {
if (this.view.isShowingForm) {
this.view.switchFormGridVisibility();
} else {
this.show();
}
},
// determine which field can be autoexpanded to use extra space
getAutoFitExpandField: function () {
var ret, i, length;
length = this.view.autoExpandFieldNames.length;
for (i = 0; i < length; i++) {
var field = this.getField(this.view.autoExpandFieldNames[i]);
if (field && field.name) {
return field.name;
}
}
ret = this.Super('getAutoFitExpandField', arguments);
return ret;
},
recordClick: function (viewer, record, recordNum, field, fieldNum, value, rawValue) {
var textDeselectInterval = setInterval(function () { //To ensure that if finally a double click (recordDoubleClick) is executed, no work is highlighted/selected
if (document.selection && document.selection.empty) {
document.selection.empty();
} else if (window.getSelection) {
var sel = window.getSelection();
sel.removeAllRanges();
}
}, 15);
setTimeout(function () {
clearInterval(textDeselectInterval);
}, 350);
var actionObject = {
target: this,
method: this.handleRecordSelection,
parameters: [viewer, record, recordNum, field, fieldNum, value, rawValue, false, this.view.isEditingGrid]
};
this.view.standardWindow.doActionAfterAutoSave(actionObject, true);
},
recordDoubleClick: function (viewer, record, recordNum, field, fieldNum, value, rawValue) {
var actionObject = {
target: this.view,
method: this.view.editRecord,
parameters: [record, false, (field ? field.name : null)]
};
this.view.standardWindow.doActionAfterAutoSave(actionObject, true);
},
resetEmptyMessage: function (criteria) {
var selectedValues, parentIsNew, oldMessage = this.emptyMessage;
criteria = criteria || this.getCriteria();
if (!this.view) {
this.emptyMessage = this.noDataEmptyMessage;
} else if (this.isGridFiltered(criteria)) {
// there can be some initial filters, but still no parent selected
if (this.view.parentView) {
selectedValues = this.view.parentView.viewGrid.getSelectedRecords();
parentIsNew = this.view.parentView.isShowingForm && this.view.parentView.viewForm.isNew;
parentIsNew = parentIsNew || (selectedValues.length === 1 && selectedValues[0]._new);
if (parentIsNew) {
this.emptyMessage = '' + OB.I18N.getLabel('OBUIAPP_ParentIsNew') + '';
} else if (!selectedValues || selectedValues.length === 0) {
this.emptyMessage = '' + OB.I18N.getLabel('OBUIAPP_NoParentSelected') + '';
} else if (selectedValues.length > 1) {
this.emptyMessage = '' + OB.I18N.getLabel('OBUIAPP_MultipleParentsSelected') + '';
} else {
this.emptyMessage = this.filterNoRecordsEmptyMessage;
}
} else {
this.emptyMessage = this.filterNoRecordsEmptyMessage;
}
} else if (this.view.isRootView) {
this.emptyMessage = this.noDataEmptyMessage;
} else {
selectedValues = this.view.parentView.viewGrid.getSelectedRecords();
parentIsNew = this.view.parentView.isShowingForm && this.view.parentView.viewForm.isNew;
parentIsNew = parentIsNew || (selectedValues.length === 1 && selectedValues[0]._new);
if (parentIsNew) {
this.emptyMessage = '' + OB.I18N.getLabel('OBUIAPP_ParentIsNew') + '';
} else if (!selectedValues || selectedValues.length === 0) {
this.emptyMessage = '' + OB.I18N.getLabel('OBUIAPP_NoParentSelected') + '';
} else if (selectedValues.length > 1) {
this.emptyMessage = '' + OB.I18N.getLabel('OBUIAPP_MultipleParentsSelected') + '';
} else {
this.emptyMessage = this.noDataEmptyMessage;
}
}
if (oldMessage !== this.emptyMessage) {
this.body.markForRedraw();
}
},
// +++++++++++++++++++++++++++++ Context menu on record click +++++++++++++++++++++++
cellContextClick: function (record, rowNum, colNum) {
var isGroupOrSummary = record && (record[this.groupSummaryRecordProperty] || record[this.gridSummaryRecordProperty]);
// don't do anything if right-clicking on a selected record
if (!this.isSelected(record)) {
this.handleRecordSelection(null, record, rowNum, null, colNum, null, null, true);
}
this.view.setAsActiveView();
if (isGroupOrSummary) {
return false;
}
var ret = this.Super('cellContextClick', arguments);
return ret;
},
makeCellContextItems: function (record, rowNum, colNum) {
var sourceWindow = this.view.standardWindow.windowId;
var menuItems = [];
var recordsSelected = this.getSelectedRecords().length > 0;
var singleSelected = this.getSelectedRecords().length === 1;
var field = this.getField(colNum);
var grid = this;
if (!this.view.hasNotChanged() || this.view.viewGrid.hasErrors()) {
menuItems.add({
title: OB.I18N.getLabel('OBUIAPP_UndoChanges'),
keyTitle: OB.KeyboardManager.Shortcuts.getProperty('keyComb.text', 'Grid_CancelChanges', 'id'),
click: function () {
grid.view.undo();
}
});
}
if (singleSelected && this.canEdit && this.isWritable(record) && !this.view.readOnly) {
menuItems.add({
title: OB.I18N.getLabel('OBUIAPP_EditInGrid'),
keyTitle: OB.KeyboardManager.Shortcuts.getProperty('keyComb.text', 'ViewGrid_EditInGrid', 'id'),
click: function () {
grid.endEditing();
if (colNum || colNum === 0) {
grid.forceFocusColumn = grid.getField(colNum).name;
}
grid.startEditing(rowNum, colNum);
}
});
}
if (!this.view.singleRecord && !this.view.readOnly && !this.isGrouped && !this.view.editOrDeleteOnly) {
menuItems.add({
title: OB.I18N.getLabel('OBUIAPP_CreateRecordInGrid'),
keyTitle: OB.KeyboardManager.Shortcuts.getProperty('keyComb.text', 'ToolBar_NewRow', 'id'),
click: function () {
grid.startEditingNew(rowNum);
}
});
}
if (singleSelected && field.canFilter) {
menuItems.add({
title: OB.I18N.getLabel('OBUIAPP_UseAsFilter'),
click: function () {
var value;
// a foreign key field, use the displayfield/identifier
if (field.fkField && field.displayField) {
value = record[field.displayField];
} else {
value = grid.getEditDisplayValue(rowNum, colNum, record);
}
// assume a date range filter item
if (isc.isA.Date(value) && field.filterEditorType === 'OBMiniDateRangeItem') {
grid.filterEditor.getEditForm().getField(field.name).setSingleDateValue(value);
} else {
grid.filterEditor.getEditForm().setValue(field.name, OB.Utilities.encodeSearchOperator(value));
}
var criteria = grid.filterEditor.getEditForm().getValuesAsCriteria();
grid.checkShowFilterFunnelIcon(criteria);
grid.filterData(criteria);
}
});
}
if (singleSelected && field.fkField) {
menuItems.add({
title: OB.I18N.getLabel('OBUIAPP_OpenOnTab'),
click: function () {
var fldName = field.name;
var dotIndex = fldName.lastIndexOf(OB.Constants.FIELDSEPARATOR);
if (dotIndex !== -1 && fldName.endsWith(OB.Constants.IDENTIFIER)) {
fldName = fldName.substring(0, dotIndex);
}
OB.Utilities.openDirectView(sourceWindow, field.refColumnName, field.targetEntity, record[fldName]);
}
});
}
if (this.view.isDeleteableTable && recordsSelected && !this.view.readOnly && !this.view.singleRecord && this.allSelectedRecordsWritable() && (this.view.standardWindow.allowDelete !== 'N')) {
menuItems.add({
title: OB.I18N.getLabel('OBUIAPP_Delete'),
keyTitle: OB.KeyboardManager.Shortcuts.getProperty('keyComb.text', 'ToolBar_Eliminate', 'id'),
click: function () {
grid.deleteSelectedRowsByToolbarIcon();
}
});
}
return menuItems;
},
deleteSelectedRowsByToolbarIcon: function () {
// The deleteSelectedRows action trigger should be the same than the toolbar button, so if this last one is overwritten,
// this delete rows logic should perform the same action than the toolbar button.
var grid = this,
isToolbarButtonFound = false,
toolbarButton, i;
if (grid.getSelectedRecords().length < 1) {
return false;
}
if (grid.view.toolBar && grid.view.toolBar.leftMembers && isc.OBToolbar.TYPE_DELETE) {
for (i = 0; i < grid.view.toolBar.leftMembers.length; i++) {
if (grid.view.toolBar.leftMembers[i].buttonType === isc.OBToolbar.TYPE_DELETE) {
isToolbarButtonFound = true;
toolbarButton = grid.view.toolBar.leftMembers[i];
if (!toolbarButton.disabled) {
toolbarButton.action();
return true;
}
break;
}
}
}
// But if the toolbar button is not found, do the default action
if (!isToolbarButtonFound) {
grid.view.deleteSelectedRows();
return true;
}
return false;
},
// +++++++++++++++++++++++++++++ Record Selection Handling +++++++++++++++++++++++
updateSelectedCountDisplay: function () {
var selection = this.getSelection(),
fld, grid = this;
var selectionLength = selection.getLength();
var newValue = ' ';
if (selectionLength > 0) {
newValue = selectionLength;
if (this.filterEditor && this.filterEditor.getEditForm()) {
fld = this.filterEditor.getEditForm().getField(this.getCheckboxField().name);
if (fld && !fld.clickForSelectedRow) {
fld.clickForSelectedRow = true;
fld.originalClick = fld.click;
fld.click = function () {
if (grid.getSelection().getLength() === 0) {
return;
}
grid.scrollToRow(grid.getRecordIndex(grid.getSelectedRecord()));
// do redraw as first columns with buttons are not drawn
grid.markForRedraw();
};
fld.itemHoverHTML = function () {
return OB.I18N.getLabel('OBUIAPP_ClickSelectedCount');
};
}
fld.textBoxStyle = fld.clickableTextBoxStyle;
fld.updateState();
}
} else {
if (this.filterEditor && this.filterEditor.getEditForm()) {
fld = this.filterEditor.getEditForm().getField(this.getCheckboxField().name);
if (fld) {
fld.textBoxStyle = fld.nonClickableTextBoxStyle;
fld.updateState();
}
}
}
if (this.filterEditor) {
this.filterEditor.getEditForm().setValue(this.getCheckboxField().name, newValue);
this.filterEditor.getEditForm().getField(this.getCheckboxField().name).defaultValue = newValue;
}
},
// note when solving selection issues in the future also
// consider using the selectionChanged method, but that
// one has as disadvantage that it is called multiple times
// for one select/deselect action
selectionUpdated: function (record, recordList) {
if ((!recordList || recordList.length === 1) && record === this.lastSelectedRecord && (this.lastSelectedRecord || record)) {
return;
}
// close any editors, but only if it is different from the one we are editing
if (this.isEditingGrid) {
var editRecord = this.getRecord(this.getEditRow());
if (editRecord !== record) {
this.closeAnyOpenEditor();
}
}
this.stopHover();
this.updateSelectedCountDisplay();
this.view.recordSelected();
if (this.getSelectedRecords() && this.getSelectedRecords().length !== 1) {
this.lastSelectedRecord = null;
} else {
this.lastSelectedRecord = this.getSelectedRecord();
}
},
selectOnMouseDown: function (record, recordNum, fieldNum, autoSaveDone) {
// don't change selection on right mouse down
var EH = isc.EventHandler,
eventType = EH.getEventType();
this.wasEditing = this.view.isEditingGrid;
// don't do anything if right-clicking on a selected record
if (EH.rightButtonDown() && this.isSelected(record)) {
return;
}
// do autosave when this is a click on a checkbox field or when this is not
// a mouse event, in other cases the autosave is done as part of the recordclick
// which is called for a mousedown also
var passToAutoSave = this.getCheckboxFieldPosition() === fieldNum || !EH.isMouseEvent(eventType);
if (!autoSaveDone && passToAutoSave) {
var actionObject = {
target: this,
method: this.selectOnMouseDown,
parameters: [record, recordNum, fieldNum, true]
};
this.view.standardWindow.doActionAfterAutoSave(actionObject, true);
return;
// only call this method in case a checkbox click was done
// in all other cases the recordClick will be called later
// anyway
// if (this.getCheckboxFieldPosition() === fieldNum) {
// this.setActionAfterAutoSave(this, this.selectOnMouseDown, arguments);
// }
}
var previousSingleRecordSelection = this.singleRecordSelection;
var currentSelectedRecordSelected = (this.getSelectedRecord() === record);
if (this.getCheckboxFieldPosition() === fieldNum) {
if (this.singleRecordSelection) {
this.deselectAllRecords(true);
}
this.singleRecordSelection = false;
this.Super('selectOnMouseDown', arguments);
// handle a special case:
// - singlerecordmode: checkbox is not checked
// - user clicks on checkbox
// in this case move to multi select mode and keep the record selected
if (previousSingleRecordSelection && currentSelectedRecordSelected) {
this.selectSingleRecord(record);
}
this.selectionUpdated();
this.markForRedraw('Selection checkboxes need to be redrawn');
} else {
// do some checking, the handleRecordSelection should only be called
// in case of keyboard navigation and not for real mouse clicks,
// these are handled by the recordClick and recordDoubleClick methods
// if this method here would also handle mouseclicks then the
// doubleClick
// event is not captured anymore
if (!EH.isMouseEvent(eventType)) {
this.handleRecordSelection(null, record, recordNum, null, fieldNum, null, null, true);
}
}
},
handleRecordSelection: function (viewer, record, recordNum, field, fieldNum, value, rawValue, fromSelectOnMouseDown) {
var wasEditing = this.wasEditing;
delete this.wasEditing;
var EH = isc.EventHandler;
var keyName = EH.getKey();
// stop editing if the user clicks out of the row
if ((this.getEditRow() || this.getEditRow() === 0) && this.getEditRow() !== recordNum) {
this.endEditing();
wasEditing = true;
}
// do nothing, click in the editrow itself
if ((this.getEditRow() || this.getEditRow() === 0) && this.getEditRow() === recordNum) {
return;
}
// if the arrow key was pressed and no ctrl/shift pressed then
// go to single select mode
var arrowKeyPressed = keyName && (keyName === isc.OBViewGrid.ARROW_UP_KEY_NAME || keyName === isc.OBViewGrid.ARROW_DOWN_KEY_NAME);
var previousSingleRecordSelection = this.singleRecordSelection;
if (arrowKeyPressed) {
if ((EH.ctrlKeyDown() && !EH.altKeyDown() && !EH.shiftKeyDown()) || (!EH.ctrlKeyDown() && !EH.altKeyDown() && EH.shiftKeyDown())) {
// move to multi-select mode, let the standard do it for us
this.singleRecordSelection = false;
} else if (!(!EH.ctrlKeyDown() && EH.altKeyDown() && EH.shiftKeyDown())) { // 'if' statement to avoid do an action when the KS to move to a child tab is fired
this.doSelectSingleRecord(record);
}
} else if (this.getCheckboxFieldPosition() === fieldNum) {
if (this.singleRecordSelection) {
this.deselectAllRecords(true);
}
// click in checkbox field is done by standard logic
// in the selectOnMouseDown
this.singleRecordSelection = false;
this.selectionUpdated();
} else if (isc.EventHandler.ctrlKeyDown() && !isc.EventHandler.altKeyDown() && !isc.EventHandler.shiftKeyDown()) {
// only do something if record clicked and not from selectOnMouseDown
// this method got called twice from one clicK: through recordClick
// and
// to selectOnMouseDown. Only handle one.
if (!fromSelectOnMouseDown) {
this.singleRecordSelection = false;
// let ctrl-click also deselect records
if (this.isSelected(record)) {
this.deselectRecord(record);
} else {
this.selectRecord(record);
}
}
} else if (!isc.EventHandler.ctrlKeyDown() && !isc.EventHandler.altKeyDown() && isc.EventHandler.shiftKeyDown()) {
this.singleRecordSelection = false;
this.selection.selectOnMouseDown(this, recordNum, fieldNum);
this.selectionUpdated(this.getSelectedRecord(), this.getSelection());
} else {
// click on the record which was already selected
this.doSelectSingleRecord(record);
// if we were editing then a single click continue edit mode
if (wasEditing) {
// set the focus in the clicked cell
this.forceFocusColumn = this.getField(fieldNum).name;
this.startEditing(recordNum, fieldNum);
}
}
this.updateSelectedCountDisplay();
this.view.toolBar.updateButtonState(true);
// mark some redraws if there are lines which don't
// have a checkbox flagged, so if we move from single record selection
// to multi record selection
if (!this.singleRecordSelection && previousSingleRecordSelection) {
this.markForRedraw('Selection checkboxes need to be redrawn');
}
},
selectRecordForEdit: function (record) {
this.Super('selectRecordForEdit', arguments);
this.doSelectSingleRecord(record);
},
doSelectSingleRecord: function (record) {
// if this record is already selected and the only one then do nothing
// note that when navigating with the arrow key that at a certain 2 are
// selected
// when going into this method therefore the extra check on length === 1
if (this.singleRecordSelection && this.getSelectedRecord() === record && this.getSelection().length === 1) {
return;
}
this.singleRecordSelection = true;
this.selectSingleRecord(record);
// deselect the checkbox in the top
var fieldNum = this.getCheckboxFieldPosition(),
field = this.fields[fieldNum];
var icon = this.checkboxFieldFalseImage || this.booleanFalseImage;
var title = this.getValueIconHTML(icon, field);
this.setFieldTitle(fieldNum, title);
},
// overridden to prevent the checkbox to be shown when only one
// record is selected.
getCellValue: function (record, recordNum, fieldNum, gridBody) {
var field = this.fields[fieldNum],
value, isEditRow = (recordNum === this.getEditRow()),
wasGrouped, func = this.getGridSummaryFunction(field),
isGroupOrSummary = record && (record[this.groupSummaryRecordProperty] || record[this.gridSummaryRecordProperty]);
// no checkbox in checkbox column for summary row
if (isGroupOrSummary && this.isCheckboxField(field)) {
return '';
}
if (!field || this.allSelected) {
return this.Super('getCellValue', arguments);
}
if (isGroupOrSummary) {
// handle count much simpler than smartclient does
// so no extra titles or formatting
if (!this.getGroupByFields().contains(field.name) && func === 'count' && (record[field.name] === 0 || record[field.name])) {
return record[field.name];
}
return this.Super('getCellValue', arguments);
}
// do all the cases which are handled in the super directly
if (this.isCheckboxField(field)) {
// NOTE: code copied from super class
var icon;
if (this.singleRecordSelection && !this.allSelected) {
// always show the false image
icon = (this.checkboxFieldFalseImage || this.booleanFalseImage);
} else {
// checked if selected, otherwise unchecked
var isSel = this.selection.isSelected(record) ? true : false;
icon = isSel ? (this.checkboxFieldTrueImage || this.booleanTrueImage) : (this.checkboxFieldFalseImage || this.booleanFalseImage);
}
// if the record is disabled, make the checkbox image disabled as well
// or if the record is new then also show disabled
if (!record || record[this.recordEnabledProperty] === false) {
icon = icon.replace('.', '_Disabled.');
}
var html = this.getValueIconHTML(icon, field);
return html;
} else {
// prevent group style behavior for edit rows
if (isEditRow && this.isGrouped) {
wasGrouped = true;
this.isGrouped = false;
}
value = this.Super('getCellValue', arguments);
if (wasGrouped) {
this.isGrouped = true;
}
return value;
}
},
getSelectedRecords: function () {
return this.getSelection();
},
// +++++++++++++++++ functions for grid editing +++++++++++++++++
startEditing: function (rowNum, colNum, suppressFocus, eCe, suppressWarning) {
var i, ret, fld, length = this.getFields().length;
// if a row is set and not a col then check if we should focus in the
// first error field
if ((rowNum || rowNum === 0) && (!colNum && colNum !== 0) && this.rowHasErrors(rowNum)) {
for (i = 0; i < length; i++) {
if (this.cellHasErrors(rowNum, i)) {
colNum = i;
break;
}
}
}
if (colNum || colNum === 0) {
this.forceFocusColumn = this.getField(colNum).name;
} else {
// set the first focused column
for (i = 0; i < length; i++) {
if (this.getFields()[i].editorProperties && this.getFields()[i].editorProperties.firstFocusedField) {
colNum = i;
}
}
if (colNum < length && this.getFields()[colNum].disabled) {
for (i = 0; i < length; i++) {
if (this.getFields()[i].editorProperties && !this.getFields()[i].disabled && this.getFields()[i].visible) {
colNum = i;
break;
}
}
}
}
// make sure that we are visible
this.scrollRecordIntoView(rowNum);
ret = this.Super('startEditing', [rowNum, colNum, suppressFocus, eCe, suppressWarning]);
return ret;
},
startEditingNew: function (rowNum) {
// several cases:
// - no current rows, add at position 0
// - row selected, add row after selected row
// - no row selected, add in the bottom
var undef, insertRow;
if (rowNum === undef) {
// nothing selected
if (!this.getSelectedRecord()) {
if (this.getTotalRows() > this.data.cachedRows) {
insertRow = 0;
} else {
insertRow = this.getTotalRows();
}
} else {
insertRow = 1 + this.getRecordIndex(this.getSelectedRecord());
}
} else {
insertRow = rowNum + 1;
}
this.createNewRecordForEditing(insertRow);
this.startEditing(insertRow);
this.recomputeCanvasComponents(insertRow);
this.view.refreshChildViews();
},
initializeEditValues: function (rowNum, colNum) {
var record = this.getRecord(rowNum);
// no record create one
if (!record) {
this.createNewRecordForEditing(rowNum);
}
return this.Super('initializeEditValues', arguments);
},
createNewRecordForEditing: function (rowNum) {
// note: the id is dummy, will be replaced when the save succeeds,
// it MUST start with _ to identify it is a temporary id
var record = {
_new: true,
id: '_' + new Date().getTime()
};
this.addToCacheData(record, rowNum);
this.scrollToRow(rowNum);
this.updateRowCountDisplay();
this.view.toolBar.updateButtonState(true);
// do it with a delay to give the system time to set the record information
this.markForRedraw();
},
addToCacheData: function (record, rowNum) {
// originalData is used when the grid is grouped
var data = this.originalData || this.data;
data.insertCacheData(record, rowNum);
},
editFailed: function (rowNum, colNum, newValues, oldValues, editCompletionEvent, dsResponse, dsRequest) {
var record = this.getRecord(rowNum),
editRow, editSession, view = this.view;
// set the default error message,
// is possibly overridden in the next call
if (record) {
record._hasValidationErrors = true;
if (!record[isc.OBViewGrid.ERROR_MESSAGE_PROP]) {
this.setRecordErrorMessage(rowNum, OB.I18N.getLabel('OBUIAPP_ErrorInFields'));
// do not automatically remove this message
this.view.messageBar.keepOnAutomaticRefresh = true;
} else {
record[this.recordBaseStyleProperty] = this.recordStyleError;
}
}
if (!this.isVisible()) {
isc.warn(OB.I18N.getLabel('OBUIAPP_TabWithErrors', [this.view.tabTitle]));
} else if (view.standardWindow.forceDialogOnFailure && !this.view.isActiveView) {
isc.warn(OB.I18N.getLabel('OBUIAPP_AutoSaveError', [this.view.tabTitle]));
}
view.standardWindow.cleanUpAutoSaveProperties();
view.updateTabTitle();
view.toolBar.updateButtonState(true);
// if nothing else got selected, select ourselves then
if (record && !this.getSelectedRecord()) {
this.selectRecord(record);
}
},
recordHasChanges: function (rowNum) {
var record = this.getRecord(rowNum);
// If a record has validation errors but had all the mandatory fields set,
// smartclient's recordHasChanges will return false, and the record will be cleared (see ListGrid.hideInlineEditor function)
// In this case recordhasChanges should return true, because the values in the grid differ with the values in the database
// See issue https://issues.openbravo.com/view.php?id=22123
if (record && record._hasValidationErrors) {
return true;
} else {
return this.Super('recordHasChanges', arguments);
}
},
editComplete: function (rowNum, colNum, newValues, oldValues, editCompletionEvent, dsResponse) {
var record = this.getRecord(rowNum),
editRow, editSession, autoSaveAction, keepSelection;
// this happens when the record change causes a group name
// change and therefore a group collapse
if (!record) {
return;
}
// a new id has been computed use that now
if (record && record._newId) {
record.id = record._newId;
delete record._newId;
}
// during save the record looses the link to the editColumnLayout,
// restore it
if (oldValues.editColumnLayout && !record.editColumnLayout) {
var editColumnLayout = oldValues.editColumnLayout;
editColumnLayout.record = record;
record.editColumnLayout = editColumnLayout;
}
if (record.editColumnLayout) {
record.editColumnLayout.editButton.setErrorState(false);
record.editColumnLayout.showEditOpen();
}
// remove any new pointer
delete record._new;
// success invoke the action, if any there
this.view.standardWindow.autoSaveDone(this.view, true);
// if nothing else got selected, select ourselves then
if (!this.getSelectedRecord()) {
this.selectRecord(record);
keepSelection = true;
this.view.refreshChildViews(keepSelection);
} else if (this.getSelectedRecord() === record) {
this.view.refreshChildViews();
}
// remove the error style/message
this.setRecordErrorMessage(rowNum, null);
// update after the error message has been removed
this.view.updateTabTitle();
this.view.toolBar.updateButtonState(true);
if (this.view.messageBar.type === isc.OBMessageBar.TYPE_ERROR) {
this.view.messageBar.hide();
}
this.view.refreshParentRecord();
if (this.getEditRow() === rowNum) {
this.getEditForm().markForRedraw();
} else {
this.refreshRow(rowNum);
}
},
undoEditSelectedRows: function () {
var selectedRecords = this.getSelectedRecords(),
toRemove = [],
i, length = selectedRecords.length;
for (i = 0; i < length; i++) {
var rowNum = this.getRecordIndex(selectedRecords[i]);
var record = selectedRecords[i];
this.Super('discardEdits', [rowNum, false, false, isc.ListGrid.PROGRAMMATIC]);
// remove the record if new
if (record._new) {
toRemove.push({
id: record.id
});
} else {
// remove the error style/msg
this.setRecordErrorMessage(rowNum, null);
}
}
this.deselectAllRecords();
this.view.refreshChildViews();
if (toRemove.length > 0) {
this.data.handleUpdate('remove', toRemove);
this.updateRowCountDisplay();
this.view.toolBar.updateButtonState(true);
}
this.view.standardWindow.cleanUpAutoSaveProperties();
this.view.updateTabTitle();
this.view.toolBar.updateButtonState(true);
},
getCellStyle: function (record, rowNum, colNum) {
// inactive, selected
if (record && record[this.recordCustomStyleProperty]) {
return record[this.recordCustomStyleProperty];
}
if (!this.view.isActiveView() && record && record[this.selection.selectionProperty]) {
return this.recordStyleSelectedViewInActive;
}
return this.Super('getCellStyle', arguments);
},
// prevent multi-line content to show strangely
// https://issues.openbravo.com/view.php?id=17531
formatDisplayValue: function (value, record, rowNum, colNum) {
var fld = this.getFields()[colNum],
index;
if (this.inCellHoverHTML || !isc.isA.String(value)) {
return value;
}
index = value.indexOf('\n');
if (index !== -1) {
return value.substring(0, index) + '...';
}
return value;
},
discardEdits: function (rowNum, colNum, dontHideEditor, editCompletionEvent, preventConfirm) {
var localArguments = arguments,
editForm = this.getEditForm(),
totalRows, me = this,
record = this.getRecord(rowNum);
if (!preventConfirm && (editForm.hasChanged || this.rowHasErrors(rowNum))) {
me.Super('discardEdits', localArguments);
// remove the record if new
if (record._new) {
totalRows = me.data.totalRows;
me.data.handleUpdate('remove', [{
id: record.id
}]);
// the total rows should be decreased
if (me.data.totalRows === totalRows) {
me.data.totalRows = me.data.totalRows - 1;
}
me.updateRowCountDisplay();
me.view.toolBar.updateButtonState(true);
me.view.refreshChildViews();
} else {
// remove the error style/msg
me.setRecordErrorMessage(rowNum, null);
}
me.view.standardWindow.cleanUpAutoSaveProperties();
// update after removing the error msg
me.view.updateTabTitle();
me.view.toolBar.updateButtonState(true);
} else {
me.Super('discardEdits', localArguments);
// remove the record if new
if (record && record._new) {
totalRows = me.data.totalRows;
me.data.handleUpdate('remove', [{
id: record.id
}]);
// the total rows should be decreased
if (me.data.totalRows === totalRows) {
me.data.totalRows = me.data.totalRows - 1;
}
me.updateRowCountDisplay();
me.view.toolBar.updateButtonState(true);
me.view.refreshChildViews();
} else {
// remove the error style/msg
me.setRecordErrorMessage(rowNum, null);
}
this.view.standardWindow.cleanUpAutoSaveProperties();
this.refreshRow(rowNum);
// update after removing the error msg
this.view.updateTabTitle();
}
},
saveEdits: function (editCompletionEvent, callback, rowNum, colNum, validateOnly, skipValidation) {
var ret = this.Super('saveEdits', arguments);
// save was not done, because there were no changes probably
if (!ret) {
this.view.standardWindow.cleanUpAutoSaveProperties();
this.view.updateTabTitle();
this.view.toolBar.updateButtonState(true);
}
return ret;
},
// check if a fic call needs to be done when leaving a cell and moving to the next
// row
// see description in saveEditvalues
cellEditEnd: function (editCompletionEvent, newValue, ficCallDone, autoSaveDone) {
var rowNum = this.getEditRow(),
colNum = this.getEditCol();
var editForm = this.getEditForm(),
editField = this.getEditField(colNum),
focusItem = (editForm ? editForm.getFocusItem() : null);
// sometimes rowNum and colnum are not set, then don't compute the next cell
var nextEditCell = ((rowNum || rowNum === 0) && (colNum || colNum === 0) ? this.getNextEditCell(rowNum, colNum, editCompletionEvent) : null);
var newRow = nextEditCell && nextEditCell[0] !== rowNum;
var enterKey = editCompletionEvent === 'enter';
// no newValue, compute it, this because in the super method there is a check
// how many arguments are passed on, sometimes the newValue is not passed in
// and then it must be recomputed, so if we then use the undefined newValue
// in the actionObject below things will go wrong
if (arguments.length < 2 && this.view.allowNewRow()) {
newValue = this.getEditValue(rowNum, colNum);
}
if (!this.view.standardWindow.isAutoSaveEnabled() && !enterKey && !autoSaveDone && newRow && (editForm.hasChanged || editForm.isNew)) {
var actionObject = {
target: this,
method: this.cellEditEnd,
parameters: [editCompletionEvent, newValue, ficCallDone, true]
};
this.view.standardWindow.doActionAfterAutoSave(actionObject, true);
return;
}
// If leaving the row...
if (editCompletionEvent === 'enter' || editCompletionEvent === 'arrow_up' || editCompletionEvent === 'arrow_down') {
// See issue https://issues.openbravo.com/view.php?id=19830
if (this.view.standardWindow.getDirtyEditForm()) {
this.view.standardWindow.getDirtyEditForm().validateForm();
}
}
this._leavingCell = true;
if (newValue) {
this.Super('cellEditEnd', [editCompletionEvent, newValue]);
} else {
this.Super('cellEditEnd', [editCompletionEvent]);
}
delete this._leavingCell;
// only needed for non picklist fields
// as picklist fields will always have picked a value
// note that focusItem updatevalue for picklist can result in extra datasource requests
if (focusItem && editField && focusItem.name === editField.name && !focusItem.hasPickList) {
focusItem.blur(focusItem.form, focusItem);
}
},
// overridden to set the enterkeyaction to nextrowstart in cases the current row
// is the last being edited
// also sets a flag which is used in canEditCell
getNextEditCell: function (rowNum, colNum, editCompletionEvent) {
var ret, i, length = this.getFields().length;
this._inGetNextEditCell = true;
// past the last row
if (editCompletionEvent === isc.ListGrid.ENTER_KEYPRESS && rowNum === (this.getTotalRows() - 1) && this.view.allowNewRow()) {
// move to the next row
ret = this.findNextEditCell(rowNum + 1, 0, 1, true, true);
// force the focus column in the first focus field
for (i = 0; i < length; i++) {
if (this.getFields()[i].editorProperties && this.getFields()[i].editorProperties.firstFocusedField) {
this.forceFocusColumn = this.getFields()[i].name;
break;
}
}
} else {
ret = this.Super('getNextEditCell', arguments);
}
// when moving between rows with the arrow keys, force the focus in the correct
// column
if (ret && ret[0] !== rowNum && this.getField(colNum) && (editCompletionEvent === isc.ListGrid.UP_ARROW_KEYPRESS || editCompletionEvent === isc.ListGrid.DOWN_ARROW_KEYPRESS) && this.view.allowNewRow()) {
this.forceFocusColumn = this.getField(colNum).name;
}
delete this._inGetNextEditCell;
return ret;
},
//used in Edit or Delete only UI pattern
setListEndEditAction: function () {
this.listEndEditAction = "done";
},
// overridden to take into account disabled at item level
// only used when computing the next edit cell
// if caneditcell returns false in other cases then smartclient
// won't even show an input but shows the display value directly
// this interferes sometimes with the very dynamic enabling
// disabling of fields by the readonlylogic
canEditCell: function (rowNum, colNum) {
var ret;
if (this._inGetNextEditCell) {
var field = this.getField(colNum);
if (field && this.getEditForm()) {
var item = this.getEditForm().getItem(field.name);
if (item && item.isDisabled()) {
return false;
}
}
}
if (!colNum && colNum !== 0) {
return false;
}
ret = this.Super('canEditCell', arguments);
return ret;
},
// saveEditedValues: when saving, first check if a FIC call needs to be done to update to the
// latest values. This can happen when the focus is in a field and the save action is
// done, at that point first try to force a fic call (handleItemChange) and if that
// indeed happens stop the saveEdit until the fic returns
saveEditedValues: function (rowNum, colNum, newValues, oldValues, editValuesID, editCompletionEvent, originalCallback, ficCallDone) {
var previousExplicitOffline, saveCallback;
if (!rowNum && rowNum !== 0) {
rowNum = this.getEditRow();
}
if (!colNum && colNum !== 0) {
colNum = this.getEditCol();
}
// nothing changed just fire the calback and bail
if (!ficCallDone && this.getEditForm() && !this.getEditForm().hasChanged && !this.getEditForm().isNew) {
if (originalCallback) {
this.fireCallback(originalCallback, "rowNum,colNum,editCompletionEvent,success", [rowNum, colNum, editCompletionEvent]);
}
return true;
}
saveCallback = function () {
if (originalCallback) {
if (this.getSelectedRecord() && this.getSelectedRecord()[OB.Constants.ID]) {
if (this.view.parentRecordId) {
if (!this.view.newRecordsAfterRefresh) {
this.view.newRecordsAfterRefresh = {};
}
if (!this.view.newRecordsAfterRefresh[this.view.parentRecordId]) {
this.view.newRecordsAfterRefresh[this.view.parentRecordId] = [];
}
this.view.newRecordsAfterRefresh[this.view.parentRecordId].push(this.getSelectedRecord()[OB.Constants.ID]);
} else {
if (!this.view.newRecordsAfterRefresh) {
this.view.newRecordsAfterRefresh = [];
}
this.view.newRecordsAfterRefresh.push(this.getSelectedRecord()[OB.Constants.ID]);
}
}
this.fireCallback(originalCallback, "rowNum,colNum,editCompletionEvent,success", [rowNum, colNum, editCompletionEvent]);
}
};
if (!ficCallDone) {
var editForm = this.getEditForm(),
focusItem = editForm.getFocusItem();
if (focusItem && !focusItem.hasPickList) {
focusItem.blur(focusItem.form, focusItem);
if (editForm.inFicCall) {
// use editValues object as the edit form will be re-used for a next row
this.setEditValue(rowNum, 'actionAfterFicReturn', {
target: this,
method: this.saveEditedValues,
parameters: [rowNum, colNum, newValues, oldValues, editValuesID, editCompletionEvent, saveCallback, true]
}, true, true);
return;
}
}
}
// reset the new values as this can have changed because of a fic call or in the blur event of the focused item
newValues = this.getEditValues(rowNum);
previousExplicitOffline = isc.Offline.explicitOffline;
isc.Offline.explicitOffline = false;
this.Super('saveEditedValues', [rowNum, colNum, newValues, oldValues, editValuesID, editCompletionEvent, saveCallback]);
isc.Offline.explicitOffline = previousExplicitOffline;
// commented out as it removes an autosave action which is done in the edit complete method
// this.view.standardWindow.setDirtyEditForm(null);
},
autoSave: function () {
// flag to force the parsing of date fields when autosaving
// see issue 20071 (https://issues.openbravo.com/view.php?id=20071)
this._autoSaving = true;
this.storeUpdatedEditorValue();
delete this._autoSaving;
this.endEditing();
},
hideInlineEditor: function (focusInBody, suppressCMHide) {
var rowNum = this.getEditRow(),
record = this.getRecord(rowNum),
editForm = this.getEditForm();
// Do not hide the inline editor if the action has been caused
// by hiding or showing a field
// See issue https://issues.openbravo.com/view.php?id=21352
if (this._hidingField || this._showingField) {
return;
}
this._hidingInlineEditor = true;
if (record && (rowNum === 0 || rowNum)) {
if (!this.rowHasErrors(rowNum)) {
record[this.recordBaseStyleProperty] = null;
}
if (record && record.editColumnLayout) {
isc.Log.logDebug('hideInlineEditor has record and editColumnLayout', 'OB');
record.editColumnLayout.showEditOpen();
} else if (this.currentEditColumnLayout) {
this.currentEditColumnLayout.showEditOpen();
} else {
isc.Log.logDebug('hideInlineEditor has NO record and editColumnLayout', 'OB');
}
this.view.isEditingGrid = false;
// Update the tab title after the record has been saved or canceled
// to get rid of the '*' in the tab title
// See https://issues.openbravo.com/view.php?id=21709
this.view.updateTabTitle();
}
// always hide the clickmask, as it needs to be re-applied
// this super call needs to be done before clearing the values
// of the form, as the form value clear will result
// in a field to be flagged with an error
var ret = this.Super('hideInlineEditor', [focusInBody, false]);
if (editForm) {
// canFocus is set when disabling a form item
// a new record needs to compute canFocus again
editForm.resetCanFocus();
// clear all values, as null values in the new row won't overwrite filled form
// values
editForm.clearValues();
// clear the errors so that they don't show up at the next row
editForm.clearErrors();
// do not save the focus item to prevent wrong validations when creating a new row
// see issue 20537 (https://issues.openbravo.com/view.php?id=20537)
editForm.setFocusItem(null);
}
delete this._hidingInlineEditor;
this.recomputeCanvasComponents(rowNum);
return ret;
},
getEditDisplayValue: function (rowNum, colNum, record) {
// somehow this extra call is needed to not restore
// the old value when the new value is null
this.storeUpdatedEditorValue();
return this.Super('getEditDisplayValue', arguments);
},
showInlineEditor: function (rowNum, colNum, newCell, newRow, suppressFocus) {
var fld;
this._showingEditor = true;
if (newRow) {
if (this.getEditForm()) {
this.getEditForm().clearErrors();
}
// if the focus does not get suppressed then the clicked field will receive focus
// and won't be disabled so the user can already start typing
suppressFocus = true;
}
var ret = this.Super('showInlineEditor', [rowNum, colNum, newCell, newRow, suppressFocus]);
if (!newRow) {
delete this._showingEditor;
return ret;
}
if (this.forceFocusColumn) {
// set the field to focus on after returning from the fic
this.getEditForm().forceFocusedField = this.forceFocusColumn;
delete this.forceFocusColumn;
} else if (colNum || colNum === 0) {
fld = this.getField(colNum);
this.getEditForm().forceFocusedField = fld.name;
}
var record = this.getRecord(rowNum);
this.view.isEditingGrid = true;
record[this.recordBaseStyleProperty] = this.baseStyleEdit;
// also called in case of new
var form = this.getEditForm();
// also make sure that the new indicator is send to the server
if (record._new) {
form.setValue('_new', true);
}
form.doEditRecordActions(false, record._new && !record._editedBefore);
record._editedBefore = true;
// must be done after doEditRecordActions
if (this.rowHasErrors(rowNum)) {
this.getEditForm().setErrors(this.getRowValidationErrors(rowNum));
this.view.standardWindow.setDirtyEditForm(form);
}
if (record && record.editColumnLayout) {
record.editColumnLayout.showSaveCancel();
}
this.view.messageBar.hide();
delete this._showingEditor;
return ret;
},
closeAnyOpenEditor: function () {
delete this.wasEditing;
// close any editors we may have
if (this.getEditRow() || this.getEditRow() === 0) {
this.endEditing();
}
},
validateField: function (field, validators, value, record, options) {
// Smartclient passes in the grid field, use the editform field
// as it contains the latest valuemap
var editField = this.getEditForm().getField(field.name) || field;
var ret = this.Super('validateField', [editField, validators, value, record, options]);
return ret;
},
refreshEditRow: function () {
var editRow = this.view.viewGrid.getEditRow(),
i, length;
if (editRow || editRow === 0) {
// don't refresh the frozen fields, this give strange
// styling issues in chrome
length = this.view.viewGrid.fields.length;
for (i = 0; i < length; i++) {
if (!this.fieldIsFrozen(i)) {
this.view.viewGrid.refreshCell(editRow, i, true);
}
}
}
},
// having a valueMap property results in setValueMap to be called
// on an item. On items with a picklist this causes calls to the
// server side
// https://issues.openbravo.com/view.php?id=16611
getEditItem: function () {
var result = this.Super('getEditItem', arguments);
if (result.hasOwnProperty('valueMap') && !result.valueMap) {
delete result.valueMap;
}
return result;
},
// set some flags to prevent the picklist fields from doing extra datasource
// requests
// https://issues.openbravo.com/view.php?id=16611
storeUpdatedEditorValue: function (suppressChange, editCol) {
this._storingUpdatedEditorValue = true;
this._preventDateParsing = true;
this.Super('storeUpdatedEditorValue', arguments);
delete this._storingUpdatedEditorValue;
delete this._preventDateParsing;
},
// the form gets recreated many times, maintain the already read valuemap
getEditorValueMap: function (field, values) {
var editRow = this.getEditRow(),
editValues = this.getEditValues(editRow);
// valuemap is set in the processcolumnvalues of the ob-view-form.js
if (editValues && editValues[field.name + '._valueMap']) {
return editValues[field.name + '._valueMap'];
}
if (this.getEditForm() && this.getEditForm().getField(field.name)) {
var liveField = this.getEditForm().getField(field.name);
if (liveField.valueMap) {
return liveField.valueMap;
}
}
return this.Super('getEditorValueMap', arguments);
},
setFieldError: function (rowNum, fieldID, errorMessage, dontDisplay) {
// if there are no errors then no need to clear
// prevents an undefined exception because also keep errors in other
// places then the editvalues._validationErrors
if (!errorMessage && !this.Super('cellHasErrors', [rowNum, fieldID])) {
return;
}
return this.Super('setFieldError', arguments);
},
cellHasErrors: function (rowNum, fieldID) {
if (this.Super('cellHasErrors', arguments)) {
return true;
}
if (this.getEditRow() === rowNum) {
var itemName = this.getEditorName(rowNum, fieldID);
if (this.getEditForm().hasFieldErrors(itemName)) {
return true;
}
// sometimes the error is there but the error message is null
if (this.getEditForm().getErrors().hasOwnProperty(itemName)) {
return true;
}
}
return false;
},
getCellErrors: function (rowNum, fieldName) {
var itemName;
var ret = this.Super('getCellErrors', arguments);
if (this.getEditRow() === rowNum) {
return this.getEditForm().getFieldErrors(itemName);
}
return ret;
},
rowHasErrors: function (rowNum, colNum) {
if (this.Super('rowHasErrors', arguments)) {
return true;
}
if (!this.getEditForm()) {
return false;
}
if (this.getEditRow() === rowNum && this.getEditForm().hasErrors()) {
return true;
}
var record = this.getRecord(rowNum);
if (record) {
return record[isc.OBViewGrid.ERROR_MESSAGE_PROP];
}
return false;
},
// we are being reshown, get new values for the combos
visibilityChanged: function (visible) {
if (visible && this.getEditRow()) {
this.getEditForm().doChangeFICCall();
}
if (!this.view.isVisible() && this.hasErrors()) {
isc.warn(OB.I18N.getLabel('OBUIAPP_TabWithErrors', [this.view.tabTitle]));
}
},
isWritable: function (record) {
return !record._readOnly;
},
allSelectedRecordsWritable: function () {
var i, length = this.getSelectedRecords().length;
for (i = 0; i < length; i++) {
var record = this.getSelectedRecords()[i];
if (!this.isWritable(record) || record._new) {
return false;
}
}
return true;
},
setRecordErrorMessage: function (rowNum, msg) {
var record = this.getRecord(rowNum);
if (!record) {
return;
}
record[isc.OBViewGrid.ERROR_MESSAGE_PROP] = msg;
if (msg) {
record[this.recordBaseStyleProperty] = this.recordStyleError;
} else {
record[this.recordBaseStyleProperty] = null;
}
if (record.editColumnLayout) {
record.editColumnLayout.editButton.setErrorState(msg);
record.editColumnLayout.editButton.setErrorMessage(msg);
}
this.refreshRow(rowNum);
},
setRecordFieldErrorMessages: function (rowNum, errors) {
var record = this.getRecord(rowNum);
if (!record) {
return;
}
if (record.editColumnLayout) {
record.editColumnLayout.editButton.setErrorState(errors);
record.editColumnLayout.editButton.setErrorMessage(OB.I18N.getLabel('OBUIAPP_ErrorInFields'));
}
this.setRowErrors(rowNum, errors);
if (errors) {
record[this.recordBaseStyleProperty] = this.recordStyleError;
} else {
record[this.recordBaseStyleProperty] = null;
}
if (this.frozenBody) {
this.frozenBody.markForRedraw();
}
this.body.markForRedraw();
},
// overridden to handle the case that the rowNum is in fact
// an edit state id
getRecord: function (rowNum) {
if (!isc.isA.Number(rowNum)) {
// an edit id
rowNum = this.getEditSessionRowNum(rowNum);
return this.Super('getRecord', [rowNum]);
}
return this.Super('getRecord', arguments);
},
// always work with fixed rowheights
// https://issues.openbravo.com/view.php?id=16307
shouldFixRowHeight: function () {
return true;
},
// needed for: https://issues.openbravo.com/view.php?id=16307
getRowHeight: function () {
return this.cellHeight;
},
// +++++++++++++++++ functions for the edit-link column +++++++++++++++++
createRecordComponent: function (record, colNum) {
var fld = this.getFields()[colNum],
isSummary = record && (record[this.groupSummaryRecordProperty] || record[this.gridSummaryRecordProperty]),
canvas, rowNum = this.getRecordIndex(record),
isEditRecord = rowNum === this.getEditRow();
// don't support record components in summary fields
if (isSummary) {
return null;
}
if (this.isEditLinkColumn(colNum)) {
var layout = isc.OBGridButtonsComponent.create({
record: record,
grid: this
});
layout.editButton.setErrorState(record[isc.OBViewGrid.ERROR_MESSAGE_PROP]);
layout.editButton.setErrorMessage(record[isc.OBViewGrid.ERROR_MESSAGE_PROP]);
record.editColumnLayout = layout;
if (record._new) {
layout.showSaveCancel();
} else {
layout.showEditOpen();
}
return layout;
} else {
return this.Super('createRecordComponent', arguments);
}
},
updateRecordComponent: function (record, colNum, component, recordChanged) {
var rowNum = this.getRecordIndex(record),
isSummary = record && (record[this.groupSummaryRecordProperty] || record[this.gridSummaryRecordProperty]),
isEditRecord = rowNum === this.getEditRow();
// don't support record components in summary fields
if (isSummary) {
return null;
}
if (component.editButton) {
if (recordChanged && component.record.editColumnLayout === component) {
component.record.editColumnLayout = null;
}
component.record = record;
record.editColumnLayout = component;
component.editButton.setErrorState(record[isc.OBViewGrid.ERROR_MESSAGE_PROP]);
component.editButton.setErrorMessage(record[isc.OBViewGrid.ERROR_MESSAGE_PROP]);
if (record._new) {
component.showSaveCancel();
} else {
component.showEditOpen();
}
} else if (isEditRecord) {
return null;
} else {
return this.Super('updateRecordComponent', arguments);
}
return component;
},
isEditLinkColumn: function (colNum) {
return this.editLinkColNum === colNum;
},
// This method forces a FIC call in case the user has changed the visible fields,
// if there is a record being edited
// This is done to load all the potentially missing combos
fieldStateChanged: function () {
var undef;
if (this.getEditRow() !== undef && this.getEditRow() !== null) {
this.getEditForm().doChangeFICCall(null, true);
}
this.Super('fieldStateChanged', arguments);
},
getFieldFromColumnName: function (columnName) {
var i, field, length, fields = this.completeFields;
length = fields.length;
for (i = 0; i < fields.length; i++) {
if (fields[i].columnName === columnName) {
field = fields[i];
break;
}
}
return field;
}
});
// = OBGridToolStripIcon =
// The icons which are inside of OBGridToolStrip
isc.ClassFactory.defineClass('OBGridToolStripIcon', isc.ImgButton);
isc.OBGridToolStripIcon.addProperties({
buttonType: null,
/* This could be: edit - form - cancel - save */
initWidget: function () {
if (this.initWidgetStyle) {
this.initWidgetStyle();
}
this.Super('initWidget', arguments);
}
});
// = OBGridToolStripSeparator =
// The separator between icons of OBGridToolStrip
isc.ClassFactory.defineClass('OBGridToolStripSeparator', isc.Img);
isc.OBGridToolStripSeparator.addProperties({});
// = OBGridButtonsComponent =
// The component which is used to create the contents of the
// edit open column in the grid
isc.ClassFactory.defineClass('OBGridButtonsComponent', isc.HLayout);
isc.OBGridButtonsComponent.addProperties({
OBGridToolStrip: null,
saveCancelLayout: null,
// the grid to which this component belongs
grid: null,
rowNum: null,
// the record to which this component belongs
record: null,
initWidget: function () {
var me = this,
formButton;
this.editButton = isc.OBGridToolStripIcon.create({
buttonType: 'edit',
originalPrompt: OB.I18N.getLabel('OBUIAPP_GridEditButtonPrompt'),
prompt: OB.I18N.getLabel('OBUIAPP_GridEditButtonPrompt'),
action: function () {
var actionObject = {
target: me,
method: me.doEdit,
parameters: null
};
me.grid.view.standardWindow.doActionAfterAutoSave(actionObject, true);
},
setErrorMessage: function (msg) {
if (msg) {
this.prompt = msg + '
' + this.originalPrompt;
} else {
this.prompt = this.originalPrompt;
}
},
showable: function () {
return !me.grid.view.readOnly && !me.record._readOnly;
},
show: function () {
if (!this.showable()) {
return;
}
return this.Super('show', arguments);
}
});
formButton = isc.OBGridToolStripIcon.create({
buttonType: 'form',
prompt: OB.I18N.getLabel('OBUIAPP_GridFormButtonPrompt'),
action: function () {
var actionObject = {
target: me,
method: me.doOpen,
parameters: null
};
me.grid.view.standardWindow.doActionAfterAutoSave(actionObject, true);
}
});
this.buttonSeparator1 = isc.OBGridToolStripSeparator.create({});
if (me.grid.view.readOnly) {
this.buttonSeparator1.visibility = 'hidden';
}
this.addMembers([formButton, this.buttonSeparator1, this.editButton]);
this.Super('initWidget', arguments);
},
addSaveCancelProgressButtons: function () {
var me = this;
// already been here
if (this.cancelButton) {
return;
}
this.progressIcon = isc.Img.create(this.grid.progressIconDefaults);
this.progressIcon.setVisibility(false);
this.addMember(this.progressIcon, 0);
// is referred to in OBViewForm.showClickMask
this.cancelButton = isc.OBGridToolStripIcon.create({
buttonType: 'cancel',
prompt: OB.I18N.getLabel('OBUIAPP_GridCancelButtonPrompt'),
action: function () {
me.doCancel();
}
});
var saveButton = isc.OBGridToolStripIcon.create({
buttonType: 'save',
prompt: OB.I18N.getLabel('OBUIAPP_GridSaveButtonPrompt'),
action: function () {
me.doSave();
}
});
this.addMembers([this.cancelButton, isc.OBGridToolStripSeparator.create({}), saveButton]);
},
toggleProgressIcon: function (toggle) {
if (toggle) {
this.hideAllMembers();
this.showMember(isc.OBViewGrid.PROGRESS);
} else {
this.hideMember(isc.OBViewGrid.PROGRESS);
if (this.grid.view.isEditingGrid) {
this.showSaveCancel();
} else {
this.showEditOpen();
}
}
},
hideAllMembers: function () {
this.hideMember(isc.OBViewGrid.ICONS.EDIT_IN_GRID);
this.hideMember(isc.OBViewGrid.ICONS.SEPARATOR1);
this.hideMember(isc.OBViewGrid.ICONS.OPEN_IN_FORM);
this.hideMember(isc.OBViewGrid.ICONS.PROGRESS);
this.hideMember(isc.OBViewGrid.ICONS.CANCEL);
this.hideMember(isc.OBViewGrid.ICONS.SEPARATOR2);
this.hideMember(isc.OBViewGrid.ICONS.SAVE);
},
showEditOpen: function () {
var offset = 0;
if (this.cancelButton) {
this.hideMember(isc.OBViewGrid.ICONS.SAVE);
this.hideMember(isc.OBViewGrid.ICONS.SEPARATOR2);
this.hideMember(isc.OBViewGrid.ICONS.CANCEL);
this.hideMember(isc.OBViewGrid.ICONS.PROGRESS);
offset = 1;
}
this.showMember(offset);
if (this.editButton.showable()) {
this.showMember(1 + offset);
this.showMember(2 + offset);
} else {
this.hideMember(1 + offset);
this.hideMember(2 + offset);
}
this.grid.currentEditColumnLayout = null;
},
showSaveCancel: function () {
this.addSaveCancelProgressButtons();
this.hideMember(isc.OBViewGrid.ICONS.EDIT_IN_GRID);
this.hideMember(isc.OBViewGrid.ICONS.SEPARATOR1);
this.hideMember(isc.OBViewGrid.ICONS.OPEN_IN_FORM);
this.hideMember(isc.OBViewGrid.ICONS.PROGRESS);
this.showMember(isc.OBViewGrid.ICONS.CANCEL);
this.showMember(isc.OBViewGrid.ICONS.SEPARATOR2);
this.showMember(isc.OBViewGrid.ICONS.SAVE);
this.grid.currentEditColumnLayout = this;
},
doEdit: function () {
this.showSaveCancel();
this.grid.selectSingleRecord(this.record);
var rowNum = this.grid.getRecordIndex(this.record);
this.grid.startEditing(rowNum);
},
doOpen: function () {
this.grid.endEditing();
this.grid.view.editRecord(this.record);
},
doSave: function () {
// note change back to editOpen is done in the editComplete event of the
// grid itself
this.grid.endEditing();
},
doCancel: function () {
this.grid.cancelEditing();
},
hideMember: function (memberNo) {
if (!this.members[memberNo]) {
return;
}
// already hidden
if (this.members[memberNo] && this.members[memberNo].visibility === isc.Canvas.HIDDEN) {
return;
}
this.Super('hideMember', arguments);
},
showMember: function (memberNo) {
if (!this.members[memberNo]) {
return;
}
// already visible
if (this.members[memberNo] && (this.members[memberNo].visibility === isc.Canvas.INHERIT || this.members[memberNo].visibility === isc.Canvas.VISIBLE)) {
return;
}
this.Super('showMember', arguments);
}
});