diff --git a/src/sql/base/browser/ui/table/plugins/copyKeybind.plugin.ts b/src/sql/base/browser/ui/table/plugins/copyKeybind.plugin.ts new file mode 100644 index 0000000000..0300ca3323 --- /dev/null +++ b/src/sql/base/browser/ui/table/plugins/copyKeybind.plugin.ts @@ -0,0 +1,62 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { Emitter, Event } from 'vs/base/common/event'; +import { isUndefinedOrNull } from 'vs/base/common/types'; + +/** + * Implements the various additional navigation keybindings we want out of slickgrid + */ +export class CopyKeybind implements Slick.Plugin { + private grid: Slick.Grid; + private handler = new Slick.EventHandler(); + + private _onCopy = new Emitter(); + public onCopy: Event = this._onCopy.event; + + public init(grid: Slick.Grid) { + this.grid = grid; + this.handler.subscribe(this.grid.onKeyDown, (e, args) => this.handleKeyDown(e, args)); + } + + public destroy() { + this.handler.unsubscribeAll(); + } + + private handleKeyDown(e: KeyboardEvent, args: Slick.OnKeyDownEventArgs): void { + let event = new StandardKeyboardEvent(e); + let handled = false; + + if (event.equals(KeyCode.KEY_C | KeyMod.CtrlCmd)) { + handled = true; + let selectionModel = this.grid.getSelectionModel(); + let ranges: Slick.Range[]; + // check to see if we can get the range from the model directly + if (selectionModel && (selectionModel).getSelectedRanges) { + ranges = (selectionModel).getSelectedRanges(); + } else { + let selectedRows = this.grid.getSelectedRows(); + let startColumn = 0; + // check for number column + if (!isUndefinedOrNull(this.grid.getColumns()[0].selectable) && !this.grid.getColumns()[0].selectable) { + startColumn = 1; + } + ranges = [new Slick.Range(selectedRows[0], startColumn, selectedRows[selectedRows.length - 1], this.grid.getColumns().length)] + } + this._onCopy.fire(ranges); + } + + if (handled) { + e.preventDefault(); + e.stopPropagation(); + e.stopImmediatePropagation(); + } + } + +} diff --git a/src/sql/parts/query/editor/actions.ts b/src/sql/parts/query/editor/actions.ts index cc1606f5b1..e2c660addb 100644 --- a/src/sql/parts/query/editor/actions.ts +++ b/src/sql/parts/query/editor/actions.ts @@ -34,6 +34,14 @@ export interface IMessagesActionContext { tree: ITree; } +function mapForNumberColumn(ranges: Slick.Range[]): Slick.Range[] { + if (ranges) { + return ranges.map(e => new Slick.Range(e.fromRow, e.fromCell - 1, e.toRow, e.toCell ? e.toCell - 1 : undefined)); + } else { + return undefined; + } +} + export class SaveResultAction extends Action { public static SAVECSV_ID = 'grid.saveAsCsv'; public static SAVECSV_LABEL = localize('saveAsCsv', 'Save As CSV'); @@ -51,13 +59,19 @@ export class SaveResultAction extends Action { id: string, label: string, icon: string, - private format: SaveFormat + private format: SaveFormat, + private accountForNumberColumn = true ) { super(id, label, icon); } public run(context: IGridActionContext): TPromise { - context.runner.serializeResults(context.batchId, context.resultId, this.format, context.selection); + if (this.accountForNumberColumn) { + context.runner.serializeResults(context.batchId, context.resultId, this.format, + mapForNumberColumn(context.selection)); + } else { + context.runner.serializeResults(context.batchId, context.resultId, this.format, context.selection); + } return TPromise.as(true); } } @@ -73,12 +87,19 @@ export class CopyResultAction extends Action { id: string, label: string, private copyHeader: boolean, + private accountForNumberColumn = true ) { super(id, label); } public run(context: IGridActionContext): TPromise { - context.runner.copyResults(context.selection, context.batchId, context.resultId, this.copyHeader); + if (this.accountForNumberColumn) { + context.runner.copyResults( + mapForNumberColumn(context.selection), + context.batchId, context.resultId, this.copyHeader); + } else { + context.runner.copyResults(context.selection, context.batchId, context.resultId, this.copyHeader); + } return TPromise.as(true); } } diff --git a/src/sql/parts/query/editor/gridPanel.ts b/src/sql/parts/query/editor/gridPanel.ts index a8ca660fb3..bae592ba96 100644 --- a/src/sql/parts/query/editor/gridPanel.ts +++ b/src/sql/parts/query/editor/gridPanel.ts @@ -38,6 +38,7 @@ import { Dimension, getContentWidth } from 'vs/base/browser/dom'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { CopyKeybind } from 'sql/base/browser/ui/table/plugins/copyKeybind.plugin'; const ROW_HEIGHT = 29; const HEADER_HEIGHT = 26; @@ -295,11 +296,24 @@ class GridTable extends Disposable implements IView { this.renderGridDataRowsRange(startIndex, count); }); let numberColumn = new RowNumberColumn({ numberOfRows: this.resultSet.rowCount }); + let copyHandler = new CopyKeybind(); + copyHandler.onCopy(e => { + new CopyResultAction(CopyResultAction.COPY_ID, CopyResultAction.COPY_LABEL, false).run({ + selection: e, + batchId: this.resultSet.batchId, + resultId: this.resultSet.id, + cell: this.table.grid.getActiveCell(), + runner: this.runner, + table: this.table, + tableState: this.state + }); + }); this.columns.unshift(numberColumn.getColumnDefinition()); this.table = this._register(new Table(tableContainer, { dataProvider: new AsyncDataProvider(collection), columns: this.columns }, { rowHeight: ROW_HEIGHT, showRowNumber: true })); this.table.setSelectionModel(this.selectionModel); this.table.registerPlugin(new MouseWheelSupport()); this.table.registerPlugin(new AutoColumnSize()); + this.table.registerPlugin(copyHandler); this.table.registerPlugin(numberColumn); this._register(this.table.onContextMenu(this.contextMenu, this)); this._register(this.table.onClick(this.onTableClick, this));