/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/gridPanel'; import { ITableStyles, ITableMouseEvent } from 'sql/base/browser/ui/table/interfaces'; import { attachTableStyler } from 'sql/platform/theme/common/styler'; import QueryRunner, { QueryGridDataProvider } from 'sql/workbench/services/query/common/queryRunner'; import { ResultSetSummary, IColumn } from 'sql/workbench/services/query/common/query'; import { VirtualizedCollection, AsyncDataProvider } from 'sql/base/browser/ui/table/asyncDataView'; import { Table } from 'sql/base/browser/ui/table/table'; import { MouseWheelSupport } from 'sql/base/browser/ui/table/plugins/mousewheelTableScroll.plugin'; import { AutoColumnSize } from 'sql/base/browser/ui/table/plugins/autoSizeColumns.plugin'; import { IGridActionContext, SaveResultAction, CopyResultAction, SelectAllGridAction, MaximizeTableAction, RestoreTableAction, ChartDataAction, VisualizerDataAction } from 'sql/workbench/contrib/query/browser/actions'; import { CellSelectionModel } from 'sql/base/browser/ui/table/plugins/cellSelectionModel.plugin'; import { RowNumberColumn } from 'sql/base/browser/ui/table/plugins/rowNumberColumn.plugin'; import { escape } from 'sql/base/common/strings'; import { hyperLinkFormatter, textFormatter } from 'sql/base/browser/ui/table/formatters'; import { AdditionalKeyBindings } from 'sql/base/browser/ui/table/plugins/additionalKeyBindings.plugin'; import { CopyKeybind } from 'sql/base/browser/ui/table/plugins/copyKeybind.plugin'; import { GridTable as HighPerfGridTable } from 'sql/workbench/contrib/query/browser/highPerfGridPanel'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { Emitter, Event } from 'vs/base/common/event'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { isUndefinedOrNull } from 'vs/base/common/types'; import { Disposable, dispose, DisposableStore } from 'vs/base/common/lifecycle'; import { range, find } from 'vs/base/common/arrays'; import { generateUuid } from 'vs/base/common/uuid'; import { ActionBar, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; import { isInDOM, Dimension } from 'vs/base/browser/dom'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IAction, Separator } from 'vs/base/common/actions'; import { ILogService } from 'vs/platform/log/common/log'; import { localize } from 'vs/nls'; import { IGridDataProvider } from 'sql/workbench/services/query/common/gridDataProvider'; import { formatDocumentWithSelectedProvider, FormattingMode } from 'vs/editor/contrib/format/format'; import { CancellationToken } from 'vs/base/common/cancellation'; import { GridPanelState, GridTableState } from 'sql/workbench/common/editor/query/gridTableState'; import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService'; import { SaveFormat } from 'sql/workbench/services/query/common/resultSerializer'; import { Progress } from 'vs/platform/progress/common/progress'; import { ScrollableView, IView } from 'sql/base/browser/ui/scrollableView/scrollableView'; import { IQueryEditorConfiguration } from 'sql/platform/query/common/query'; import { Orientation } from 'vs/base/browser/ui/splitview/splitview'; const ROW_HEIGHT = 29; const HEADER_HEIGHT = 26; const MIN_GRID_HEIGHT_ROWS = 8; const ESTIMATED_SCROLL_BAR_HEIGHT = 15; const BOTTOM_PADDING = 15; const ACTIONBAR_WIDTH = 36; // minimum height needed to show the full actionbar const ACTIONBAR_HEIGHT = 120; // this handles min size if rows is greater than the min grid visible rows const MIN_GRID_HEIGHT = (MIN_GRID_HEIGHT_ROWS * ROW_HEIGHT) + HEADER_HEIGHT + ESTIMATED_SCROLL_BAR_HEIGHT; export class GridPanel extends Disposable { private container = document.createElement('div'); private scrollableView: ScrollableView; private tables: Array | HighPerfGridTable> = []; private tableDisposable = this._register(new DisposableStore()); private queryRunnerDisposables = this._register(new DisposableStore()); private runner: QueryRunner; private maximizedGrid: GridTable | HighPerfGridTable; private _state: GridPanelState | undefined; private readonly optimized = this.configurationService.getValue('queryEditor').results.optimizedTable; constructor( @IConfigurationService private readonly configurationService: IConfigurationService, @IInstantiationService private readonly instantiationService: IInstantiationService, @ILogService private readonly logService: ILogService, @IThemeService private readonly themeService: IThemeService, ) { super(); this.scrollableView = new ScrollableView(this.container, { scrollDebouce: this.optimized ? 0 : undefined }); this.scrollableView.onDidScroll(e => { if (this.state && this.scrollableView.length !== 0) { this.state.scrollPosition = e.scrollTop; } }); } public render(container: HTMLElement): void { this.container.style.width = '100%'; this.container.style.height = '100%'; container.appendChild(this.container); } public layout(size: Dimension): void { this.scrollableView.layout(size.height, size.width); } public focus(): void { // will need to add logic to save the focused grid and focus that this.tables[0].focus(); } public set queryRunner(runner: QueryRunner) { this.queryRunnerDisposables.clear(); this.reset(); this.runner = runner; this.queryRunnerDisposables.add(this.runner.onResultSet(this.onResultSet, this)); this.queryRunnerDisposables.add(this.runner.onResultSetUpdate(this.updateResultSet, this)); this.queryRunnerDisposables.add(this.runner.onQueryStart(() => { if (this.state) { this.state.tableStates = []; } this.reset(); })); this.addResultSet(this.runner.batchSets.reduce((p, e) => { if (this.configurationService.getValue('queryEditor').results.streaming) { p = p.concat(e.resultSetSummaries); } else { p = p.concat(e.resultSetSummaries.filter(c => c.complete)); } return p; }, [])); if (this.state && this.state.scrollPosition) { this.scrollableView.setScrollTop(this.state.scrollPosition); } } public resetScrollPosition(): void { this.scrollableView.setScrollTop(this.state.scrollPosition); } private onResultSet(resultSet: ResultSetSummary | ResultSetSummary[]) { let resultsToAdd: ResultSetSummary[]; if (!Array.isArray(resultSet)) { resultsToAdd = [resultSet]; } else { resultsToAdd = resultSet.splice(0); } const sizeChanges = () => { this.tables.map(t => { t.state.canBeMaximized = this.tables.length > 1; }); if (this.state && this.state.scrollPosition) { this.scrollableView.setScrollTop(this.state.scrollPosition); } }; if (this.configurationService.getValue('queryEditor').results.streaming) { this.addResultSet(resultsToAdd); sizeChanges(); } else { resultsToAdd = resultsToAdd.filter(e => e.complete); if (resultsToAdd.length > 0) { this.addResultSet(resultsToAdd); } sizeChanges(); } } private updateResultSet(resultSet: ResultSetSummary | ResultSetSummary[]) { let resultsToUpdate: ResultSetSummary[]; if (!Array.isArray(resultSet)) { resultsToUpdate = [resultSet]; } else { resultsToUpdate = resultSet.splice(0); } const sizeChanges = () => { if (this.state && this.state.scrollPosition) { this.scrollableView.setScrollTop(this.state.scrollPosition); } }; if (this.configurationService.getValue('queryEditor').results.streaming) { for (let set of resultsToUpdate) { let table = find(this.tables, t => t.resultSet.batchId === set.batchId && t.resultSet.id === set.id); if (table) { table.updateResult(set); } else { this.logService.warn('Got result set update request for non-existant table'); } } sizeChanges(); } else { resultsToUpdate = resultsToUpdate.filter(e => e.complete); if (resultsToUpdate.length > 0) { this.addResultSet(resultsToUpdate); } sizeChanges(); } } private addResultSet(resultSet: ResultSetSummary[]) { const tables: Array | HighPerfGridTable> = []; for (const set of resultSet) { // ensure we aren't adding a resultSet that is already visible if (find(this.tables, t => t.resultSet.batchId === set.batchId && t.resultSet.id === set.id)) { continue; } let tableState: GridTableState; if (this.state) { tableState = find(this.state.tableStates, e => e.batchId === set.batchId && e.resultId === set.id); } if (!tableState) { tableState = new GridTableState(set.id, set.batchId); if (this.state) { this.state.tableStates.push(tableState); } } let table: GridTable | HighPerfGridTable; if (this.optimized) { table = this.instantiationService.createInstance(HighPerfGridTable, this.runner, set, tableState); } else { table = this.instantiationService.createInstance(GridTable, this.runner, set, tableState); } this.tableDisposable.add(tableState.onMaximizedChange(e => { if (e) { this.maximizeTable(table.id); } else { this.minimizeTables(); } })); this.tableDisposable.add(attachTableStyler(table, this.themeService)); tables.push(table); } this.tables = this.tables.concat(tables); // turn-off special-case process when only a single table is being displayed if (this.tables.length > 1) { for (let i = 0; i < this.tables.length; ++i) { this.tables[i].isOnlyTable = false; } } if (isUndefinedOrNull(this.maximizedGrid)) { this.scrollableView.addViews(tables); } } public clear() { this.reset(); this.state = undefined; } private reset() { this.scrollableView.clear(); dispose(this.tables); this.tableDisposable.clear(); this.tables = []; this.maximizedGrid = undefined; } private maximizeTable(tableid: string): void { if (!find(this.tables, t => t.id === tableid)) { return; } for (let i = this.tables.length - 1; i >= 0; i--) { if (this.tables[i].id === tableid) { const selectedTable = this.tables[i]; selectedTable.state.maximized = true; this.maximizedGrid = selectedTable; this.scrollableView.clear(); this.scrollableView.addViews([selectedTable]); break; } } } private minimizeTables(): void { if (this.maximizedGrid) { this.maximizedGrid.state.maximized = false; this.maximizedGrid = undefined; this.scrollableView.clear(); this.scrollableView.addViews(this.tables); } } public set state(val: GridPanelState) { this._state = val; if (this.state) { this.tables.map(t => { let state = find(this.state.tableStates, s => s.batchId === t.resultSet.batchId && s.resultId === t.resultSet.id); if (!state) { this.state.tableStates.push(t.state); } if (state) { t.state = state; } }); } } public get state() { return this._state; } public dispose() { dispose(this.tables); this.tables = undefined; super.dispose(); } } export interface IDataSet { rowCount: number; columnInfo: IColumn[]; } export interface IGridTableOptions { actionOrientation: ActionsOrientation; } export abstract class GridTableBase extends Disposable implements IView { private table: Table; private actionBar: ActionBar; private container = document.createElement('div'); private selectionModel = new CellSelectionModel(); private styles: ITableStyles; private currentHeight: number; private dataProvider: AsyncDataProvider; private columns: Slick.Column[]; private rowNumberColumn: RowNumberColumn; private _onDidChange = new Emitter(); public readonly onDidChange: Event = this._onDidChange.event; public id = generateUuid(); readonly element: HTMLElement = this.container; protected tableContainer: HTMLElement; private _state: GridTableState; private scrolled = false; private visible = false; private rowHeight: number; public isOnlyTable: boolean = true; // this handles if the row count is small, like 4-5 rows protected get maxSize(): number { return ((this.resultSet.rowCount) * this.rowHeight) + HEADER_HEIGHT + ESTIMATED_SCROLL_BAR_HEIGHT; } public focus(): void { if (!this.table.activeCell) { this.table.setActiveCell(0, 1); this.selectionModel.setSelectedRanges([new Slick.Range(0, 1)]); } this.table.focus(); } constructor( state: GridTableState, protected _resultSet: ResultSetSummary, private readonly options: IGridTableOptions = { actionOrientation: ActionsOrientation.VERTICAL }, @IContextMenuService private readonly contextMenuService: IContextMenuService, @IInstantiationService protected readonly instantiationService: IInstantiationService, @IEditorService private readonly editorService: IEditorService, @IUntitledTextEditorService private readonly untitledEditorService: IUntitledTextEditorService, @IConfigurationService private readonly configurationService: IConfigurationService ) { super(); let config = this.configurationService.getValue<{ rowHeight: number }>('resultsGrid'); this.rowHeight = config && config.rowHeight ? config.rowHeight : ROW_HEIGHT; this.state = state; this.container.style.width = '100%'; this.container.style.height = '100%'; this.columns = this.resultSet.columnInfo.map((c, i) => { let isLinked = c.isXml || c.isJson; return >{ id: i.toString(), name: c.columnName === 'Microsoft SQL Server 2005 XML Showplan' ? localize('xmlShowplan', "XML Showplan") : escape(c.columnName), field: i.toString(), formatter: isLinked ? hyperLinkFormatter : textFormatter, width: this.state.columnSizes && this.state.columnSizes[i] ? this.state.columnSizes[i] : undefined }; }); } abstract get gridDataProvider(): IGridDataProvider; public get resultSet(): ResultSetSummary { return this._resultSet; } public onDidInsert() { if (!this.table) { this.build(); } this.visible = true; let collection = new VirtualizedCollection( 50, index => this.placeholdGenerator(index), this.resultSet.rowCount, (offset, count) => this.loadData(offset, count) ); collection.setCollectionChangedCallback((startIndex, count) => { this.renderGridDataRowsRange(startIndex, count); }); this.dataProvider.dataRows = collection; this.table.updateRowCount(); this.setupState(); } public onDidRemove() { this.visible = false; let collection = new VirtualizedCollection( 50, index => this.placeholdGenerator(index), 0, () => Promise.resolve([]) ); this.dataProvider.dataRows = collection; this.table.updateRowCount(); // when we are removed slickgrid acts badly so we need to account for that this.scrolled = false; } // actionsOrientation controls the orientation (horizontal or vertical) of the actionBar private build(): void { let actionBarContainer = document.createElement('div'); // Create a horizontal actionbar if orientation passed in is HORIZONTAL if (this.options.actionOrientation === ActionsOrientation.HORIZONTAL) { actionBarContainer.className = 'grid-panel action-bar horizontal'; this.container.appendChild(actionBarContainer); } this.tableContainer = document.createElement('div'); this.tableContainer.className = 'grid-panel'; this.tableContainer.style.display = 'inline-block'; this.tableContainer.style.width = `calc(100% - ${ACTIONBAR_WIDTH}px)`; this.container.appendChild(this.tableContainer); let collection = new VirtualizedCollection( 50, index => this.placeholdGenerator(index), 0, () => Promise.resolve([]) ); collection.setCollectionChangedCallback((startIndex, count) => { this.renderGridDataRowsRange(startIndex, count); }); this.rowNumberColumn = new RowNumberColumn({ numberOfRows: this.resultSet.rowCount }); let copyHandler = new CopyKeybind(); copyHandler.onCopy(e => { new CopyResultAction(CopyResultAction.COPY_ID, CopyResultAction.COPY_LABEL, false).run(this.generateContext()); }); this.columns.unshift(this.rowNumberColumn.getColumnDefinition()); let tableOptions: Slick.GridOptions = { rowHeight: this.rowHeight, showRowNumber: true, forceFitColumns: false, defaultColumnWidth: 120 }; this.dataProvider = new AsyncDataProvider(collection); this.table = this._register(new Table(this.tableContainer, { dataProvider: this.dataProvider, columns: this.columns }, tableOptions)); this.table.setTableTitle(localize('resultsGrid', "Results grid")); this.table.setSelectionModel(this.selectionModel); this.table.registerPlugin(new MouseWheelSupport()); this.table.registerPlugin(new AutoColumnSize({ autoSizeOnRender: !this.state.columnSizes && this.configurationService.getValue('resultsGrid.autoSizeColumns'), maxWidth: this.configurationService.getValue('resultsGrid.maxColumnWidth') })); this.table.registerPlugin(copyHandler); this.table.registerPlugin(this.rowNumberColumn); this.table.registerPlugin(new AdditionalKeyBindings()); this._register(this.table.onContextMenu(this.contextMenu, this)); this._register(this.table.onClick(this.onTableClick, this)); //This listener is used for correcting auto-scroling when clicking on the header for reszing. this._register(this.table.onHeaderClick(this.onHeaderClick, this)); if (this.styles) { this.table.style(this.styles); } // If the actionsOrientation passed in is "VERTICAL" (or no actionsOrientation is passed in at all), create a vertical actionBar if (this.options.actionOrientation === ActionsOrientation.VERTICAL) { actionBarContainer.className = 'grid-panel action-bar vertical'; actionBarContainer.style.width = ACTIONBAR_WIDTH + 'px'; this.container.appendChild(actionBarContainer); } let context: IGridActionContext = { gridDataProvider: this.gridDataProvider, table: this.table, tableState: this.state, batchId: this.resultSet.batchId, resultId: this.resultSet.id }; this.actionBar = new ActionBar(actionBarContainer, { orientation: this.options.actionOrientation, context: context }); // update context before we run an action this.selectionModel.onSelectedRangesChanged.subscribe(e => { this.actionBar.context = this.generateContext(); }); this.rebuildActionBar(); this.selectionModel.onSelectedRangesChanged.subscribe(e => { if (this.state) { this.state.selection = this.selectionModel.getSelectedRanges(); } }); this.table.grid.onScroll.subscribe((e, data) => { if (!this.visible) { // If the grid is not set up yet it can get scroll events resetting the top to 0px, // so ignore those events return; } if (!this.scrolled && (this.state.scrollPositionY || this.state.scrollPositionX) && isInDOM(this.container)) { this.scrolled = true; this.restoreScrollState(); } if (this.state && isInDOM(this.container)) { this.state.scrollPositionY = data.scrollTop; this.state.scrollPositionX = data.scrollLeft; } }); // we need to remove the first column since this is the row number this.table.onColumnResize(() => { let columnSizes = this.table.grid.getColumns().slice(1).map(v => v.width); this.state.columnSizes = columnSizes; }); this.table.grid.onActiveCellChanged.subscribe(e => { if (this.state) { this.state.activeCell = this.table.grid.getActiveCell(); } }); } private restoreScrollState() { if (this.state.scrollPositionX || this.state.scrollPositionY) { this.table.grid.scrollTo(this.state.scrollPositionY); this.table.grid.getContainerNode().children[3].scrollLeft = this.state.scrollPositionX; } } private setupState() { // change actionbar on maximize change this._register(this.state.onMaximizedChange(this.rebuildActionBar, this)); this._register(this.state.onCanBeMaximizedChange(this.rebuildActionBar, this)); this.restoreScrollState(); this.rebuildActionBar(); // Setting the active cell resets the selection so save it here let savedSelection = this.state.selection; if (this.state.activeCell) { this.table.setActiveCell(this.state.activeCell.row, this.state.activeCell.cell); } if (savedSelection) { this.selectionModel.setSelectedRanges(savedSelection); } } public get state(): GridTableState { return this._state; } public set state(val: GridTableState) { this._state = val; } private onHeaderClick(event: ITableMouseEvent) { //header clicks must be accounted for as they force the table to scroll to the top; this.scrolled = false; } private onTableClick(event: ITableMouseEvent) { // account for not having the number column let column = this.resultSet.columnInfo[event.cell.cell - 1]; // handle if a showplan link was clicked if (column && (column.isXml || column.isJson)) { this.gridDataProvider.getRowData(event.cell.row, 1).then(async d => { let value = d.rows[0][event.cell.cell - 1]; let content = value.displayValue; const input = this.untitledEditorService.create({ mode: column.isXml ? 'xml' : 'json', initialValue: content }); await input.load(); await this.instantiationService.invokeFunction(formatDocumentWithSelectedProvider, input.textEditorModel, FormattingMode.Explicit, Progress.None, CancellationToken.None); return this.editorService.openEditor(input); }); } } public updateResult(resultSet: ResultSetSummary) { this._resultSet = resultSet; if (this.table && this.visible) { this.dataProvider.length = resultSet.rowCount; this.table.updateRowCount(); } this._onDidChange.fire(undefined); } private generateContext(cell?: Slick.Cell): IGridActionContext { const selection = this.selectionModel.getSelectedRanges(); return { cell, selection, gridDataProvider: this.gridDataProvider, table: this.table, tableState: this.state, selectionModel: this.selectionModel }; } private rebuildActionBar() { let actions = this.getCurrentActions(); this.actionBar.clear(); this.actionBar.push(actions, { icon: true, label: false }); } protected abstract getCurrentActions(): IAction[]; protected abstract getContextActions(): IAction[]; // The actionsOrientation passed in controls the actionBar orientation public layout(size?: number): void { if (!size) { size = this.currentHeight; } else { this.currentHeight = size; } // Table is always called with Orientation as VERTICAL this.table.layout(size, Orientation.VERTICAL); } public get minimumSize(): number { // clamp between ensuring we can show the actionbar, while also making sure we don't take too much space // if there is only one table then allow a minimum size of ROW_HEIGHT return this.isOnlyTable ? ROW_HEIGHT : Math.max(Math.min(this.maxSize, MIN_GRID_HEIGHT), ACTIONBAR_HEIGHT + BOTTOM_PADDING); } public get maximumSize(): number { return Math.max(this.maxSize, ACTIONBAR_HEIGHT + BOTTOM_PADDING); } private loadData(offset: number, count: number): Thenable { return this.gridDataProvider.getRowData(offset, count).then(response => { if (!response) { return []; } return response.rows.map(r => { let dataWithSchema = {}; // skip the first column since its a number column for (let i = 1; i < this.columns.length; i++) { dataWithSchema[this.columns[i].field] = { displayValue: r[i - 1].displayValue, ariaLabel: escape(r[i - 1].displayValue), isNull: r[i - 1].isNull }; } return dataWithSchema as T; }); }); } private contextMenu(e: ITableMouseEvent): void { const { cell } = e; this.contextMenuService.showContextMenu({ getAnchor: () => e.anchor, getActions: () => { let actions: IAction[] = [ new SelectAllGridAction(), new Separator() ]; let contributedActions: IAction[] = this.getContextActions(); if (contributedActions && contributedActions.length > 0) { actions.push(...contributedActions); actions.push(new Separator()); } actions.push( new CopyResultAction(CopyResultAction.COPY_ID, CopyResultAction.COPY_LABEL, false), new CopyResultAction(CopyResultAction.COPYWITHHEADERS_ID, CopyResultAction.COPYWITHHEADERS_LABEL, true) ); if (this.state.canBeMaximized) { if (this.state.maximized) { actions.splice(1, 0, new RestoreTableAction()); } else { actions.splice(1, 0, new MaximizeTableAction()); } } return actions; }, getActionsContext: () => { return this.generateContext(cell); } }); } private placeholdGenerator(index: number): any { return {}; } private renderGridDataRowsRange(startIndex: number, count: number): void { // let editor = this.table.getCellEditor(); // let oldValue = editor ? editor.getValue() : undefined; // let wasValueChanged = editor ? editor.isValueChanged() : false; this.invalidateRange(startIndex, startIndex + count); // let activeCell = this._grid.getActiveCell(); // if (editor && activeCell.row >= startIndex && activeCell.row < startIndex + count) { // if (oldValue && wasValueChanged) { // editor.setValue(oldValue); // } // } } private invalidateRange(start: number, end: number): void { let refreshedRows = range(start, end); if (this.table) { this.table.invalidateRows(refreshedRows, true); } } public style(styles: ITableStyles) { if (this.table) { this.table.style(styles); } else { this.styles = styles; } } public dispose() { this.container.remove(); if (this.table) { this.table.dispose(); } if (this.actionBar) { this.actionBar.dispose(); } super.dispose(); } } class GridTable extends GridTableBase { private _gridDataProvider: IGridDataProvider; constructor( private _runner: QueryRunner, resultSet: ResultSetSummary, state: GridTableState, @IContextMenuService contextMenuService: IContextMenuService, @IInstantiationService instantiationService: IInstantiationService, @IContextKeyService private contextKeyService: IContextKeyService, @IEditorService editorService: IEditorService, @IUntitledTextEditorService untitledEditorService: IUntitledTextEditorService, @IConfigurationService configurationService: IConfigurationService ) { super(state, resultSet, undefined, contextMenuService, instantiationService, editorService, untitledEditorService, configurationService); this._gridDataProvider = this.instantiationService.createInstance(QueryGridDataProvider, this._runner, resultSet.batchId, resultSet.id); } get gridDataProvider(): IGridDataProvider { return this._gridDataProvider; } protected getCurrentActions(): IAction[] { let actions = []; if (this.state.canBeMaximized) { if (this.state.maximized) { actions.splice(1, 0, new RestoreTableAction()); } else { actions.splice(1, 0, new MaximizeTableAction()); } } actions.push( this.instantiationService.createInstance(SaveResultAction, SaveResultAction.SAVECSV_ID, SaveResultAction.SAVECSV_LABEL, SaveResultAction.SAVECSV_ICON, SaveFormat.CSV), this.instantiationService.createInstance(SaveResultAction, SaveResultAction.SAVEEXCEL_ID, SaveResultAction.SAVEEXCEL_LABEL, SaveResultAction.SAVEEXCEL_ICON, SaveFormat.EXCEL), this.instantiationService.createInstance(SaveResultAction, SaveResultAction.SAVEJSON_ID, SaveResultAction.SAVEJSON_LABEL, SaveResultAction.SAVEJSON_ICON, SaveFormat.JSON), this.instantiationService.createInstance(SaveResultAction, SaveResultAction.SAVEXML_ID, SaveResultAction.SAVEXML_LABEL, SaveResultAction.SAVEXML_ICON, SaveFormat.XML), this.instantiationService.createInstance(ChartDataAction) ); if (this.contextKeyService.getContextKeyValue('showVisualizer')) { actions.push(this.instantiationService.createInstance(VisualizerDataAction, this._runner)); } return actions; } protected getContextActions(): IAction[] { return [ this.instantiationService.createInstance(SaveResultAction, SaveResultAction.SAVECSV_ID, SaveResultAction.SAVECSV_LABEL, SaveResultAction.SAVECSV_ICON, SaveFormat.CSV), this.instantiationService.createInstance(SaveResultAction, SaveResultAction.SAVEEXCEL_ID, SaveResultAction.SAVEEXCEL_LABEL, SaveResultAction.SAVEEXCEL_ICON, SaveFormat.EXCEL), this.instantiationService.createInstance(SaveResultAction, SaveResultAction.SAVEJSON_ID, SaveResultAction.SAVEJSON_LABEL, SaveResultAction.SAVEJSON_ICON, SaveFormat.JSON), this.instantiationService.createInstance(SaveResultAction, SaveResultAction.SAVEXML_ID, SaveResultAction.SAVEXML_LABEL, SaveResultAction.SAVEXML_ICON, SaveFormat.XML), ]; } }