Compare commits

..

41 Commits

Author SHA1 Message Date
Karl Burtram
9717b7516d Bump Agent, Import and Profiler extensions versions (#2330) 2018-08-24 19:16:11 -04:00
AlexFsmn
b92c23df2b Added functionality for adding any file to import wizard (#2329)
#2316
2018-08-24 19:01:22 -04:00
Aditya Bist
df617f19e0 Feature: Usage metrics (#2227)
* added user stats telemetry

* finished daily, weekly and monthly events

* code review comments

* added wekly and monthly start dates

* fixed the day condition using testing
2018-08-24 12:11:22 -07:00
Alan Ren
fb565c2548 insights widget accessibility support (#2324) 2018-08-24 10:22:58 -07:00
Kevin Cunnane
2bfc3a6c85 Support position property in editor component (#2314)
* Support position property in editor component
- This needs to be set directly on the editor component so cannot just use CSSStyles feature
- Given its importance to this scenario, it also warrants a dedicated property.

* Fix window resize when action bar clicked

* Renamed per Abbie's suggestion

* Changed name to clarify the comments
2018-08-23 17:32:13 -07:00
Alan Ren
4ab5d84b94 hide the backup restore commands from command palette (#2317) 2018-08-23 17:08:54 -07:00
Karl Burtram
154213b705 Bump Electron to 1.7.16 (#2312) 2018-08-23 19:55:40 -04:00
Anthony Dresser
4cce29ea9d change sqlops to be dev dep (#2311) 2018-08-23 14:15:08 -07:00
Alan Yu
9d5d00aa8f Updated Profiler readme with getting started instructions 2018-08-23 13:09:13 -07:00
Karl Burtram
70d47c1757 Prompt to create Profiler session per server (#2303)
* Null check getXEventSessions return value

* Profiler state WIP

* Fix various bugs getting session state
2018-08-23 15:33:23 -04:00
Anthony Dresser
befa34790f Refactor results grid (#2147)
* got a basic ui

* working on message panel

* done with messages moving to grids

* formatting

* working on multiple grids

* it does work

* styling

* formatting

* formatting

* fixed reset methods

* moved for scrollable

* formatting

* fixing scrolling

* making progress

* formatting

* fixed scrolling

* fix horizontal scrolling and size

* fix columns for tables

* integrate view item

* implementing heightmap scrolling

* add context menu and fix scrolling

* formatting

* revert slickgrid

* add actions to message pane

* formatting

* formatting

* bottom padding for tables

* minimized and maximized table actions

* add timestamp

* added batch start message  with selection

* updating

* formatting

* formatting

* fix execution time

* formatting

* fix problems

* fix rendering issues, add icons

* formatting

* formatting

* added commit change

* fix performance, message scrolling, etc

* formatting

* formatting

* fixing performance

* formatting

* update package

* tring to fix bugs

* reworking

* the problem is the 1st sash is always the first sash visible

* remove resizing from grid panels

* add missing files

* trying to get edit to work

* fix editdata

* formatting

* update angular2-slickgrid
2018-08-23 12:32:47 -07:00
Kevin Cunnane
84da9d289b Support vertical orientation for toolbar (#2308)
* Support vertical orientation for toolbar modelview component

* Add tab support for each button

* Fix padding and simplify styles
2018-08-23 10:54:20 -07:00
Matt Irvine
7a30d535e8 Make it possible to tab to result export buttons (#2302) 2018-08-23 10:27:48 -07:00
Kevin Cunnane
199701d26b Temporarily roll back SqlToolsService update to fix #2304 (#2305)
- Credential Service and Azure / Resource Provider Services are all broken in 1.5.0-alpha.29 so rolling back until fixed
2018-08-22 20:03:23 -07:00
Alan Ren
3ac2cfa528 fix the active cell's bottom border disappearing issue (#2301) 2018-08-22 16:56:32 -07:00
Karl Burtram
78d68aa1b9 Fix typo in SQL Tools Service config.json 2018-08-22 13:57:52 -07:00
Cory Rivera
8042b78f1e Check whether page is valid before enabling next button. (#2293) 2018-08-22 12:12:33 -07:00
Matt Irvine
000d064276 Show no account message after deleting all azure accounts (#2294) 2018-08-22 11:57:29 -07:00
Karl Burtram
600a78f35f Update SQL Tools Service to 1.5.0-alpha.29 2018-08-21 22:12:04 -07:00
Alan Ren
6f5e4c30dc fix edit data issue (#2285) 2018-08-21 18:29:21 -07:00
Karl Burtram
8e7457911e Bump DMP client to pick-up build connection string LIB (#2284) 2018-08-21 20:17:16 -04:00
Aditya Bist
43b3207937 changed desktop icon flag (#2281) 2018-08-21 16:50:30 -07:00
Abbie Petchtes
22f2151c21 fix the layout issue in model view (#2280) 2018-08-21 16:16:15 -07:00
Matt Irvine
7c744f2307 Put newline between multiple selections when copying results (#2279) 2018-08-21 14:04:46 -07:00
Karl Burtram
08d57fed86 Check-in current XLF files (#2275)
* Check-in current XLF files

* Remove duplicate IDs from SQL.XLF file

* Add comment why line is being disabled
2018-08-21 16:57:22 -04:00
AlexFsmn
6e8a0fe0ef Fixed background issue when copying a chart to clipboard (#2215)
#183
2018-08-20 19:35:49 -04:00
Anthony Dresser
6668ec4b5d Fix accessibility bugs in Chart Viewer and Advanced properties (#2240)
* fix accessibility bugs in chart viewer

* add advanced properties

* add comments for changes in vs
2018-08-20 16:19:29 -07:00
Alan Ren
08a8288293 fix the server group disapearing issue #1835 (#2269)
* fix the server group disapearing issue #1835

* address review comments
2018-08-20 15:43:16 -04:00
Matt Irvine
3001640eec Fix getUriForConnection API returning wrong URIs (#2202)
* Fix connection URI api to return working URI

* run tsfmt

* Keep using hand-built connection string for now in import

* Use connection ID instead of URI to get connection string
2018-08-20 15:41:06 -04:00
Aditya Bist
efa3658ced added preview messages (#2208) 2018-08-20 15:01:29 -04:00
AlexFsmn
92bc253cf7 Fixed problem where vertical charts didn't display labels correctly. (#2263)
#2017
2018-08-20 15:01:00 -04:00
AlexFsmn
a190190843 Fixed Initial values for charts to match visuals (#2266)
#2265
2018-08-20 14:59:43 -04:00
Kevin Cunnane
9c40bd1a23 Fix error where rename controller wasn't in InstantiationService (#2243) 2018-08-20 11:58:40 -07:00
Abbie Petchtes
dc2193138d fix the layout issue in model view dialog (#2242) 2018-08-20 11:53:47 -07:00
Anthony Dresser
033c8cb8b1 Adds build connection info feature (#2192)
* connection string

* formatting

* change serailize reponse type to match connect params

* add connection string serialization

* readd the connection string to the connection widget

* format

* remove unnecessary change

* update serializer to require provider

* update name of function

* fix function name

* bump dataprotocol and sqltools

* revert unnecessary change

* remove more unnecessary chagnes

* bump sqltoolsserivce

* adde configuration for auto parsing the clipboard
2018-08-20 14:50:16 -04:00
Aditya Bist
21c4429c6e Agent: Added support for high contrast theme (#2229)
* added support for high contrast theme

* initial theming

* programmatically change theme colors in agent
2018-08-20 10:08:00 -07:00
AlexFsmn
348a96b033 Renamed chart option labels (#2264)
#2262
2018-08-19 21:12:16 -04:00
AlexFsmn
0c930d7c0f Added feature for opening file after exporting to CSV/XLS/JSON & query files (#2216)
* Fix #746.
Added prompt for opening saved file location/file after save.
This fix includes saving of JSON/CSV/Excel & saving of a new SQL file.

* Changed var to let.
Moved code from vs dir to sql.
Removed support for showing file location after file save.
(Will be moved to another PR).
#746
2018-08-17 20:28:00 -04:00
Karl Burtram
98aca2b988 Bump SQL Ops to 0.32.6 (#2256) 2018-08-17 20:24:39 -04:00
Karl Burtram
8d7f497e0c Switch back DB icon but use default colors (#2254) 2018-08-17 18:49:03 -04:00
Leila Lali
1b6328b451 reverting a change in declarative table (#2246) 2018-08-17 14:50:41 -07:00
128 changed files with 8490 additions and 2311 deletions

View File

@@ -1,3 +1,3 @@
disturl "https://atom.io/download/electron"
target "1.7.12"
target "1.7.16"
runtime "electron"

View File

@@ -414,7 +414,7 @@ function packageTask(platform, arch, opts) {
license,
watermark,
api,
// {{SQL CARBON EDIT}}
// {{SQL CARBON EDIT}}
copiedModules,
dataApi,
sources,
@@ -528,7 +528,9 @@ gulp.task('vscode-translations-push-test', ['optimize-vscode'], function () {
gulp.src(pathToMetadata).pipe(i18n.createXlfFilesForCoreBundle()),
gulp.src(pathToSetup).pipe(i18n.createXlfFilesForIsl()),
gulp.src(pathToExtensions).pipe(i18n.createXlfFilesForExtensions())
).pipe(i18n.findObsoleteResources(apiHostname, apiName, apiToken)
// {{SQL CARBON EDIT}}
// disable since function makes calls to VS Code Transifex API
// ).pipe(i18n.findObsoleteResources(apiHostname, apiName, apiToken)
).pipe(vfs.dest('../vscode-transifex-input'));
});

View File

@@ -60,7 +60,7 @@ Type: files; Name: "{app}\resources\app\Credits_45.0.2454.85.html"; Check: IsNot
Type: filesandordirs; Name: "{app}\_"
[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: checkedonce;
Name: "quicklaunchicon"; Description: "{cm:CreateQuickLaunchIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked; OnlyBelowVersion: 0,6.1
Name: "associatewithfiles"; Description: "{cm:AssociateWithFiles,{#NameShort}}"; GroupDescription: "{cm:Other}"; Flags: unchecked
Name: "addtopath"; Description: "{cm:AddToPath}"; GroupDescription: "{cm:Other}"

View File

@@ -2,7 +2,7 @@
"name": "agent",
"displayName": "SQL Server Agent",
"description": "Manage and troubleshoot SQL Server Agent jobs",
"version": "0.32.4",
"version": "0.32.7",
"publisher": "Microsoft",
"preview": true,
"license": "https://raw.githubusercontent.com/Microsoft/sqlopsstudio/master/LICENSE.txt",

View File

@@ -367,11 +367,18 @@ export class AlertDialog extends AgentDialog<AlertData> {
title: AlertDialog.NewJobButtonLabel
}], { componentWidth: '100%'}).component();
let previewTag = view.modelBuilder.text()
.withProperties({
value: 'Feature Preview'
}).component();
this.notifyOperatorsCheckBox = view.modelBuilder.checkBox()
.withProperties({
label: AlertDialog.NotifyOperatorsTextBoxLabel
}).component();
this.notifyOperatorsCheckBox.enabled = false;
this.operatorsTable = view.modelBuilder.table()
.withProperties({
columns: [
@@ -423,6 +430,9 @@ export class AlertDialog extends AgentDialog<AlertData> {
}, {
component: executeJobContainer,
title: ''
}, {
component: previewTag,
title: ''
}, {
component: this.notifyOperatorsCheckBox,
title: ''

View File

@@ -125,7 +125,6 @@ export class JobDialog extends AgentDialog<JobData> {
this.initializeSchedulesTab();
this.initializeNotificationsTab();
this.dialog.content = [this.generalTab, this.stepsTab, this.schedulesTab, this.alertsTab, this.notificationsTab];
this.dialog.registerCloseValidator(() => {
this.updateModel();
let validationResult = this.model.validate();
@@ -194,6 +193,10 @@ export class JobDialog extends AgentDialog<JobData> {
private initializeStepsTab() {
this.stepsTab.registerContent(async view => {
let previewTag = view.modelBuilder.text()
.withProperties({
value: 'Feature Preview'
}).component();
this.stepsTable = view.modelBuilder.table()
.withProperties({
columns: [
@@ -246,8 +249,14 @@ export class JobDialog extends AgentDialog<JobData> {
width: 80
}).component();
this.stepsTable.enabled = false;
let formModel = view.modelBuilder.formContainer()
.withFormItems([{
component: previewTag,
title: ''
},
{
component: this.stepsTable,
title: this.JobStepsTopLabelString,
actions: [this.moveStepUpButton, this.moveStepDownButton, this.newStepButton, this.editStepButton, this.deleteStepButton]
@@ -258,6 +267,10 @@ export class JobDialog extends AgentDialog<JobData> {
private initializeAlertsTab() {
this.alertsTab.registerContent(async view => {
let previewTag = view.modelBuilder.text()
.withProperties({
value: 'Feature Preview'
}).component();
this.alertsTable = view.modelBuilder.table()
.withProperties({
columns: [
@@ -282,6 +295,9 @@ export class JobDialog extends AgentDialog<JobData> {
let formModel = view.modelBuilder.formContainer()
.withFormItems([{
component: previewTag,
title: ''
}, {
component: this.alertsTable,
title: this.AlertsTopLabelString,
actions: [this.newAlertButton]

View File

@@ -368,6 +368,10 @@ export class OperatorDialog extends AgentDialog<OperatorData> {
private initializeNotificationTab() {
this.notificationsTab.registerContent(async view => {
let previewTag = view.modelBuilder.text()
.withProperties({
value: 'Feature Preview'
}).component();
this.alertsTable = view.modelBuilder.table()
.withProperties({
columns: [
@@ -381,6 +385,9 @@ export class OperatorDialog extends AgentDialog<OperatorData> {
let formModel = view.modelBuilder.formContainer()
.withFormItems([{
component: previewTag,
title: ''
}, {
component: this.alertsTable,
title: OperatorDialog.AlertsTableLabel
}]).withLayout({ width: '100%' }).component();

View File

@@ -2,7 +2,7 @@
"name": "import",
"displayName": "SQL Server Import",
"description": "Imports data from a flat file.",
"version": "0.0.1",
"version": "0.0.2",
"publisher": "Microsoft",
"preview": true,
"engines": {
@@ -80,7 +80,7 @@
}
},
"dependencies": {
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.2.5",
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.2.7",
"opener": "^1.4.3",
"service-downloader": "github:anthonydresser/service-downloader#0.1.4",
"vscode-extension-telemetry": "^0.0.5",

View File

@@ -245,7 +245,8 @@ export class FileConfigPage extends ImportPage {
canSelectMany: false,
openLabel: localize('flatFileImport.openFile', 'Open'),
filters: {
'Files': ['csv', 'txt']
'CSV/TXT Files': ['csv', 'txt'],
'All Files': ['*']
}
}
);

View File

@@ -60,9 +60,9 @@ core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
"dataprotocol-client@github:Microsoft/sqlops-dataprotocolclient#0.2.5":
version "0.2.5"
resolved "https://codeload.github.com/Microsoft/sqlops-dataprotocolclient/tar.gz/6aca9c6850d2b2e1e59f09d67e15922f5acec5ea"
"dataprotocol-client@github:Microsoft/sqlops-dataprotocolclient#0.2.7":
version "0.2.6"
resolved "https://codeload.github.com/Microsoft/sqlops-dataprotocolclient/tar.gz/85653d8b305af8aef334728d71f07bdc240dfcb7"
dependencies:
vscode-languageclient "3.5.1"

View File

@@ -0,0 +1,4 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1

View File

@@ -0,0 +1,4 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1

View File

@@ -17,6 +17,14 @@
"compile": "gulp compile-extension:mssql-client",
"update-grammar": "node ../../build/npm/update-grammar.js Microsoft/vscode-mssql syntaxes/SQL.plist ./syntaxes/sql.tmLanguage.json"
},
"dependencies": {
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.2.7",
"opener": "^1.4.3",
"service-downloader": "github:anthonydresser/service-downloader#0.1.4",
"vscode-extension-telemetry": "^0.0.15"
},
"devDependencies": {
},
"contributes": {
"languages": [
{
@@ -656,13 +664,5 @@
}
]
}
},
"dependencies": {
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.2.5",
"opener": "^1.4.3",
"service-downloader": "github:anthonydresser/service-downloader#0.1.4",
"vscode-extension-telemetry": "^0.0.15"
},
"devDependencies": {
}
}

View File

@@ -1,6 +1,6 @@
{
"downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/v{#version#}/microsoft.sqltools.servicelayer-{#fileName#}",
"version": "1.5.0-alpha.27",
"version": "1.5.0-alpha.28",
"downloadFileNames": {
"Windows_86": "win-x86-netcoreapp2.1.zip",
"Windows_64": "win-x64-netcoreapp2.1.zip",

View File

@@ -64,9 +64,9 @@ core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
"dataprotocol-client@github:Microsoft/sqlops-dataprotocolclient#0.2.5":
version "0.2.5"
resolved "https://codeload.github.com/Microsoft/sqlops-dataprotocolclient/tar.gz/6aca9c6850d2b2e1e59f09d67e15922f5acec5ea"
"dataprotocol-client@github:Microsoft/sqlops-dataprotocolclient#0.2.7":
version "0.2.6"
resolved "https://codeload.github.com/Microsoft/sqlops-dataprotocolclient/tar.gz/85653d8b305af8aef334728d71f07bdc240dfcb7"
dependencies:
vscode-languageclient "3.5.1"

View File

@@ -10,6 +10,17 @@ Common SQL Profiler use-cases taken from https://docs.microsoft.com/en-us/sql/to
- Monitoring the performance of SQL Server to tune workloads.
- Correlating performance counters to diagnose problems.
## Getting Started:
To launch SQL Server Profiler, you have to first make a connection to a server.
Open Profiler by pressing **Alt+P** on Windows, and **Ctrl+Alt+P** on macOS.
To Start/Stop Profiler, click the Start button or press **Alt+S** on Windows, or **Ctrl+Alt+S** on macOS.
Otherwise, open the command palette and type 'Profiler.'
For more info, [check out our documentation.](https://docs.microsoft.com/en-us/sql/sql-operations-studio/sql-server-profiler-extension?view=sql-server-2017)
## SQL Server Profiler 0.1.1 Release
The SQL Server Profiler for SQL Operations Studio *Preview* extension is now available. This is the initial preview release for a new lightweight XEvent-based profiler. The SQL Server Profiler extension tries to make it simple to quickly trace server activity for troubleshooting and monitoring.

View File

@@ -2,7 +2,7 @@
"name": "profiler",
"displayName": "SQL Server Profiler",
"description": "SQL Server Profiler for SQL Operations Studio",
"version": "0.1.3",
"version": "0.1.5",
"publisher": "Microsoft",
"preview": true,
"license": "https://raw.githubusercontent.com/Microsoft/sqlopsstudio/master/LICENSE.txt",

View File

@@ -232,11 +232,11 @@
},
"_db_light": {
"fontCharacter": "\\E01C",
"fontColor": "#dd4b78"
"fontColor": "#bfc2c1"
},
"_db": {
"fontCharacter": "\\E01C",
"fontColor": "#f55385"
"fontColor": "#d4d7d6"
},
"_default_light": {
"fontCharacter": "\\E01D",
@@ -1411,7 +1411,7 @@
"rust": "_rust",
"scss": "_sass",
"shellscript": "_shell",
"sql": "_default",
"sql": "_db",
"swift": "_swift",
"typescript": "_typescript",
"typescriptreact": "_react",
@@ -1616,7 +1616,7 @@
"rust": "_rust_light",
"scss": "_sass_light",
"shellscript": "_shell_light",
"sql": "_default_light",
"sql": "_db_light",
"swift": "_swift_light",
"typescript": "_typescript_light",
"typescriptreact": "_react_light",

View File

@@ -1,6 +1,6 @@
{
"name": "sqlops",
"version": "0.32.5",
"version": "0.32.6",
"distro": "8c3e97e3425cc9814496472ab73e076de2ba99ee",
"author": {
"name": "Microsoft Corporation"
@@ -34,7 +34,7 @@
"@angular/router": "~4.1.3",
"@angular/upgrade": "~4.1.3",
"angular2-grid": "2.0.6",
"angular2-slickgrid": "github:Microsoft/angular2-slickgrid#1.4.3",
"angular2-slickgrid": "github:Microsoft/angular2-slickgrid#1.4.4",
"applicationinsights": "0.18.0",
"chart.js": "^2.6.0",
"fast-plist": "0.1.2",

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,589 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file original="extensions/agent/out/dialogs/scheduleDialog" source-language="en" datatype="plaintext"><body>
<trans-unit id="scheduleDialog.newSchedule">
<source xml:lang="en">New Schedule</source>
</trans-unit>
<trans-unit id="scheduleDialog.ok">
<source xml:lang="en">OK</source>
</trans-unit>
<trans-unit id="scheduleDialog.cancel">
<source xml:lang="en">Cancel</source>
</trans-unit>
<trans-unit id="scheduleDialog.scheduleName">
<source xml:lang="en">Schedule Name</source>
</trans-unit>
<trans-unit id="scheduleDialog.schedules">
<source xml:lang="en">Schedules</source>
</trans-unit>
</body></file>
<file original="extensions/agent/out/dialogs/proxyDialog" source-language="en" datatype="plaintext"><body>
<trans-unit id="createProxy.createProxy">
<source xml:lang="en">Create Proxy</source>
</trans-unit>
<trans-unit id="createProxy.editProxy">
<source xml:lang="en">Edit Proxy</source>
</trans-unit>
<trans-unit id="createProxy.General">
<source xml:lang="en">General</source>
</trans-unit>
<trans-unit id="createProxy.ProxyName">
<source xml:lang="en">Proxy name</source>
</trans-unit>
<trans-unit id="createProxy.CredentialName">
<source xml:lang="en">Credential name</source>
</trans-unit>
<trans-unit id="createProxy.Description">
<source xml:lang="en">Description</source>
</trans-unit>
<trans-unit id="createProxy.SubsystemName">
<source xml:lang="en">Subsystem</source>
</trans-unit>
<trans-unit id="createProxy.OperatingSystem">
<source xml:lang="en">Operating system (CmdExec)</source>
</trans-unit>
<trans-unit id="createProxy.ReplicationSnapshot">
<source xml:lang="en">Replication Snapshot</source>
</trans-unit>
<trans-unit id="createProxy.ReplicationTransactionLog">
<source xml:lang="en">Replication Transaction-Log Reader</source>
</trans-unit>
<trans-unit id="createProxy.ReplicationDistributor">
<source xml:lang="en">Replication Distributor</source>
</trans-unit>
<trans-unit id="createProxy.ReplicationMerge">
<source xml:lang="en">Replication Merge</source>
</trans-unit>
<trans-unit id="createProxy.ReplicationQueueReader">
<source xml:lang="en">Replication Queue Reader</source>
</trans-unit>
<trans-unit id="createProxy.SSASQueryLabel">
<source xml:lang="en">SQL Server Analysis Services Query</source>
</trans-unit>
<trans-unit id="createProxy.SSASCommandLabel">
<source xml:lang="en">SQL Server Analysis Services Command</source>
</trans-unit>
<trans-unit id="createProxy.SSISPackage">
<source xml:lang="en">SQL Server Integration Services Package</source>
</trans-unit>
<trans-unit id="createProxy.PowerShell">
<source xml:lang="en">PowerShell</source>
</trans-unit>
<trans-unit id="createProxy.subSystemHeading">
<source xml:lang="en">Active to the following subsytems</source>
</trans-unit>
</body></file>
<file original="extensions/agent/out/dialogs/pickScheduleDialog" source-language="en" datatype="plaintext"><body>
<trans-unit id="pickSchedule.jobSchedules">
<source xml:lang="en">Job Schedules</source>
</trans-unit>
<trans-unit id="pickSchedule.ok">
<source xml:lang="en">OK</source>
</trans-unit>
<trans-unit id="pickSchedule.cancel">
<source xml:lang="en">Cancel</source>
</trans-unit>
<trans-unit id="pickSchedule.scheduleName">
<source xml:lang="en">Schedule Name</source>
</trans-unit>
<trans-unit id="pickSchedule.schedules">
<source xml:lang="en">Schedules</source>
</trans-unit>
</body></file>
<file original="extensions/agent/out/dialogs/operatorDialog" source-language="en" datatype="plaintext"><body>
<trans-unit id="createOperator.createOperator">
<source xml:lang="en">Create Operator</source>
</trans-unit>
<trans-unit id="createOperator.editOperator">
<source xml:lang="en">Edit Operator</source>
</trans-unit>
<trans-unit id="createOperator.General">
<source xml:lang="en">General</source>
</trans-unit>
<trans-unit id="createOperator.Notifications">
<source xml:lang="en">Notifications</source>
</trans-unit>
<trans-unit id="createOperator.Name">
<source xml:lang="en">Name</source>
</trans-unit>
<trans-unit id="createOperator.Enabled">
<source xml:lang="en">Enabled</source>
</trans-unit>
<trans-unit id="createOperator.EmailName">
<source xml:lang="en">E-mail Name</source>
</trans-unit>
<trans-unit id="createOperator.PagerEmailName">
<source xml:lang="en">Pager E-mail Name</source>
</trans-unit>
<trans-unit id="createOperator.PagerMondayCheckBox">
<source xml:lang="en">Monday</source>
</trans-unit>
<trans-unit id="createOperator.PagerTuesdayCheckBox">
<source xml:lang="en">Tuesday</source>
</trans-unit>
<trans-unit id="createOperator.PagerWednesdayCheckBox">
<source xml:lang="en">Wednesday</source>
</trans-unit>
<trans-unit id="createOperator.PagerThursdayCheckBox">
<source xml:lang="en">Thursday</source>
</trans-unit>
<trans-unit id="createOperator.PagerFridayCheckBox">
<source xml:lang="en">Friday </source>
</trans-unit>
<trans-unit id="createOperator.PagerSaturdayCheckBox">
<source xml:lang="en">Saturday</source>
</trans-unit>
<trans-unit id="createOperator.PagerSundayCheckBox">
<source xml:lang="en">Sunday</source>
</trans-unit>
<trans-unit id="createOperator.workdayBegin">
<source xml:lang="en">Workday begin</source>
</trans-unit>
<trans-unit id="createOperator.workdayEnd">
<source xml:lang="en">Workday end</source>
</trans-unit>
<trans-unit id="createOperator.PagerDutySchedule">
<source xml:lang="en">Pager on duty schdule</source>
</trans-unit>
<trans-unit id="createOperator.AlertListHeading">
<source xml:lang="en">Alert list</source>
</trans-unit>
<trans-unit id="createOperator.AlertNameColumnLabel">
<source xml:lang="en">Alert name</source>
</trans-unit>
<trans-unit id="createOperator.AlertEmailColumnLabel">
<source xml:lang="en">E-mail</source>
</trans-unit>
<trans-unit id="createOperator.AlertPagerColumnLabel">
<source xml:lang="en">Pager</source>
</trans-unit>
</body></file>
<file original="extensions/agent/out/dialogs/jobStepDialog" source-language="en" datatype="plaintext"><body>
<trans-unit id="jobStepDialog.newJobStep">
<source xml:lang="en">New Job Step</source>
</trans-unit>
<trans-unit id="jobStepDialog.fileBrowserTitle">
<source xml:lang="en">Locate Database Files - </source>
</trans-unit>
<trans-unit id="jobStepDialog.ok">
<source xml:lang="en">OK</source>
</trans-unit>
<trans-unit id="jobStepDialog.cancel">
<source xml:lang="en">Cancel</source>
</trans-unit>
<trans-unit id="jobStepDialog.general">
<source xml:lang="en">General</source>
</trans-unit>
<trans-unit id="jobStepDialog.advanced">
<source xml:lang="en">Advanced</source>
</trans-unit>
<trans-unit id="jobStepDialog.open">
<source xml:lang="en">Open...</source>
</trans-unit>
<trans-unit id="jobStepDialog.parse">
<source xml:lang="en">Parse</source>
</trans-unit>
<trans-unit id="jobStepDialog.next">
<source xml:lang="en">Next</source>
</trans-unit>
<trans-unit id="jobStepDialog.previous">
<source xml:lang="en">Previous</source>
</trans-unit>
<trans-unit id="jobStepDialog.successParse">
<source xml:lang="en">The command was successfully parsed.</source>
</trans-unit>
<trans-unit id="jobStepDialog.failParse">
<source xml:lang="en">The command failed.</source>
</trans-unit>
<trans-unit id="jobStepDialog.blankStepName">
<source xml:lang="en">The step name cannot be left blank</source>
</trans-unit>
<trans-unit id="jobStepDialog.stepNameLabel">
<source xml:lang="en">Step Name</source>
</trans-unit>
<trans-unit id="jobStepDialog.typeLabel">
<source xml:lang="en">Type</source>
</trans-unit>
<trans-unit id="jobStepDialog.runAsLabel">
<source xml:lang="en">Run as</source>
</trans-unit>
<trans-unit id="jobStepDialog.databaseLabel">
<source xml:lang="en">Database</source>
</trans-unit>
<trans-unit id="jobStepDialog.commandLabel">
<source xml:lang="en">Command</source>
</trans-unit>
<trans-unit id="jobStepDialog.successAction">
<source xml:lang="en">On success action</source>
</trans-unit>
<trans-unit id="jobStepDialog.failureAction">
<source xml:lang="en">On failure action</source>
</trans-unit>
<trans-unit id="jobStepDialog.runAsUser">
<source xml:lang="en">Run as user</source>
</trans-unit>
<trans-unit id="jobStepDialog.retryAttempts">
<source xml:lang="en">Retry Attempts</source>
</trans-unit>
<trans-unit id="jobStepDialog.retryInterval">
<source xml:lang="en">Retry Interval (minutes)</source>
</trans-unit>
<trans-unit id="jobStepDialog.logToTable">
<source xml:lang="en">Log to table</source>
</trans-unit>
<trans-unit id="jobStepDialog.appendExistingTableEntry">
<source xml:lang="en">Append output to exisiting entry in table</source>
</trans-unit>
<trans-unit id="jobStepDialog.includeStepOutputHistory">
<source xml:lang="en">Include step output in history</source>
</trans-unit>
<trans-unit id="jobStepDialog.outputFile">
<source xml:lang="en">Output File</source>
</trans-unit>
<trans-unit id="jobStepDialog.appendOutputToFile">
<source xml:lang="en">Append output to existing file</source>
</trans-unit>
<trans-unit id="jobStepDialog.selectedPath">
<source xml:lang="en">Selected path</source>
</trans-unit>
<trans-unit id="jobStepDialog.filesOfType">
<source xml:lang="en">Files of type</source>
</trans-unit>
<trans-unit id="jobStepDialog.fileName">
<source xml:lang="en">File name</source>
</trans-unit>
<trans-unit id="jobStepDialog.allFiles">
<source xml:lang="en">All Files (*)</source>
</trans-unit>
<trans-unit id="jobStepDialog.TSQL">
<source xml:lang="en">Transact-SQL script (T-SQL)</source>
</trans-unit>
<trans-unit id="jobStepDialog.agentServiceAccount">
<source xml:lang="en">SQL Server Agent Service Account</source>
</trans-unit>
<trans-unit id="jobStepDialog.nextStep">
<source xml:lang="en">Go to the next step</source>
</trans-unit>
<trans-unit id="jobStepDialog.quitJobSuccess">
<source xml:lang="en">Quit the job reporting success</source>
</trans-unit>
<trans-unit id="jobStepDialog.quitJobFailure">
<source xml:lang="en">Quit the job reporting failure</source>
</trans-unit>
</body></file>
<file original="extensions/agent/out/dialogs/jobDialog" source-language="en" datatype="plaintext"><body>
<trans-unit id="jobDialog.general">
<source xml:lang="en">General</source>
</trans-unit>
<trans-unit id="jobDialog.steps">
<source xml:lang="en">Steps</source>
</trans-unit>
<trans-unit id="jobDialog.schedules">
<source xml:lang="en">Schedules</source>
</trans-unit>
<trans-unit id="jobDialog.alerts">
<source xml:lang="en">Alerts</source>
</trans-unit>
<trans-unit id="jobDialog.notifications">
<source xml:lang="en">Notifications</source>
</trans-unit>
<trans-unit id="jobDialog.blankJobNameError">
<source xml:lang="en">The name of the job cannot be blank.</source>
</trans-unit>
<trans-unit id="jobDialog.name">
<source xml:lang="en">Name</source>
</trans-unit>
<trans-unit id="jobDialog.owner">
<source xml:lang="en">Owner</source>
</trans-unit>
<trans-unit id="jobDialog.category">
<source xml:lang="en">Category</source>
</trans-unit>
<trans-unit id="jobDialog.description">
<source xml:lang="en">Description</source>
</trans-unit>
<trans-unit id="jobDialog.enabled">
<source xml:lang="en">Enabled</source>
</trans-unit>
<trans-unit id="jobDialog.jobStepList">
<source xml:lang="en">Job step list</source>
</trans-unit>
<trans-unit id="jobDialog.step">
<source xml:lang="en">Step</source>
</trans-unit>
<trans-unit id="jobDialog.type">
<source xml:lang="en">Type</source>
</trans-unit>
<trans-unit id="jobDialog.onSuccess">
<source xml:lang="en">On Success</source>
</trans-unit>
<trans-unit id="jobDialog.onFailure">
<source xml:lang="en">On Failure</source>
</trans-unit>
<trans-unit id="jobDialog.new">
<source xml:lang="en">New...</source>
</trans-unit>
<trans-unit id="jobDialog.edit">
<source xml:lang="en">Edit</source>
</trans-unit>
<trans-unit id="jobDialog.delete">
<source xml:lang="en">Delete</source>
</trans-unit>
<trans-unit id="jobDialog.moveUp">
<source xml:lang="en">Move Step Up</source>
</trans-unit>
<trans-unit id="jobDialog.moveDown">
<source xml:lang="en">Move Step Up</source>
</trans-unit>
<trans-unit id="jobDialog.notificationsTabTop">
<source xml:lang="en">Actions to perform when the job completes</source>
</trans-unit>
<trans-unit id="jobDialog.email">
<source xml:lang="en">Email</source>
</trans-unit>
<trans-unit id="jobDialog.page">
<source xml:lang="en">Page</source>
</trans-unit>
<trans-unit id="jobDialog.eventLogCheckBoxLabel">
<source xml:lang="en">Write to the Windows Application event log</source>
</trans-unit>
<trans-unit id="jobDialog.deleteJobLabel">
<source xml:lang="en">Automatically delete job</source>
</trans-unit>
<trans-unit id="jobDialog.schedulesaLabel">
<source xml:lang="en">Schedules list</source>
</trans-unit>
<trans-unit id="jobDialog.pickSchedule">
<source xml:lang="en">Pick Schedule</source>
</trans-unit>
<trans-unit id="jobDialog.scheduleNameLabel">
<source xml:lang="en">Schedule Name</source>
</trans-unit>
<trans-unit id="jobDialog.alertsList">
<source xml:lang="en">Alerts list</source>
</trans-unit>
<trans-unit id="jobDialog.newAlert">
<source xml:lang="en">New Alert</source>
</trans-unit>
<trans-unit id="jobDialog.alertNameLabel">
<source xml:lang="en">Alert Name</source>
</trans-unit>
<trans-unit id="jobDialog.newJob">
<source xml:lang="en">New Job</source>
</trans-unit>
<trans-unit id="jobDialog.editJob">
<source xml:lang="en">Edit Job</source>
</trans-unit>
</body></file>
<file original="extensions/agent/out/dialogs/alertDialog" source-language="en" datatype="plaintext"><body>
<trans-unit id="alertDialog.createAlert">
<source xml:lang="en">Create Alert</source>
</trans-unit>
<trans-unit id="alertDialog.editAlert">
<source xml:lang="en">Edit Alert</source>
</trans-unit>
<trans-unit id="alertDialog.General">
<source xml:lang="en">General</source>
</trans-unit>
<trans-unit id="alertDialog.Response">
<source xml:lang="en">Response</source>
</trans-unit>
<trans-unit id="alertDialog.Options">
<source xml:lang="en">Options</source>
</trans-unit>
<trans-unit id="alertDialog.eventAlert">
<source xml:lang="en">Event alert definition</source>
</trans-unit>
<trans-unit id="alertDialog.Name">
<source xml:lang="en">Name</source>
</trans-unit>
<trans-unit id="alertDialog.Type">
<source xml:lang="en">Type</source>
</trans-unit>
<trans-unit id="alertDialog.Enabled">
<source xml:lang="en">Enabled</source>
</trans-unit>
<trans-unit id="alertDialog.DatabaseName">
<source xml:lang="en">Database name</source>
</trans-unit>
<trans-unit id="alertDialog.ErrorNumber">
<source xml:lang="en">Error number</source>
</trans-unit>
<trans-unit id="alertDialog.Severity">
<source xml:lang="en">Severity</source>
</trans-unit>
<trans-unit id="alertDialog.RaiseAlertContains">
<source xml:lang="en">Raise alert when message contains</source>
</trans-unit>
<trans-unit id="alertDialog.MessageText">
<source xml:lang="en">Message text</source>
</trans-unit>
<trans-unit id="alertDialog.Severity001">
<source xml:lang="en">001 - Miscellaneous System Information</source>
</trans-unit>
<trans-unit id="alertDialog.Severity002">
<source xml:lang="en">002 - Reserved</source>
</trans-unit>
<trans-unit id="alertDialog.Severity003">
<source xml:lang="en">003 - Reserved</source>
</trans-unit>
<trans-unit id="alertDialog.Severity004">
<source xml:lang="en">004 - Reserved</source>
</trans-unit>
<trans-unit id="alertDialog.Severity005">
<source xml:lang="en">005 - Reserved</source>
</trans-unit>
<trans-unit id="alertDialog.Severity006">
<source xml:lang="en">006 - Reserved</source>
</trans-unit>
<trans-unit id="alertDialog.Severity007">
<source xml:lang="en">007 - Notification: Status Information</source>
</trans-unit>
<trans-unit id="alertDialog.Severity008">
<source xml:lang="en">008 - Notification: User Intervention Required</source>
</trans-unit>
<trans-unit id="alertDialog.Severity009">
<source xml:lang="en">009 - User Defined</source>
</trans-unit>
<trans-unit id="alertDialog.Severity010">
<source xml:lang="en">010 - Information</source>
</trans-unit>
<trans-unit id="alertDialog.Severity011">
<source xml:lang="en">011 - Specified Database Object Not Found</source>
</trans-unit>
<trans-unit id="alertDialog.Severity012">
<source xml:lang="en">012 - Unused</source>
</trans-unit>
<trans-unit id="alertDialog.Severity013">
<source xml:lang="en">013 - User Transaction Syntax Error</source>
</trans-unit>
<trans-unit id="alertDialog.Severity014">
<source xml:lang="en">014 - Insufficient Permission</source>
</trans-unit>
<trans-unit id="alertDialog.Severity015">
<source xml:lang="en">015 - Syntax Error in SQL Statements</source>
</trans-unit>
<trans-unit id="alertDialog.Severity016">
<source xml:lang="en">016 - Miscellaneous User Error</source>
</trans-unit>
<trans-unit id="alertDialog.Severity017">
<source xml:lang="en">017 - Insufficient Resources</source>
</trans-unit>
<trans-unit id="alertDialog.Severity018">
<source xml:lang="en">018 - Nonfatal Internal Error</source>
</trans-unit>
<trans-unit id="alertDialog.Severity019">
<source xml:lang="en">019 - Fatal Error in Resource</source>
</trans-unit>
<trans-unit id="alertDialog.Severity020">
<source xml:lang="en">020 - Fatal Error in Current Process</source>
</trans-unit>
<trans-unit id="alertDialog.Severity021">
<source xml:lang="en">021 - Fatal Error in Database Processes</source>
</trans-unit>
<trans-unit id="alertDialog.Severity022">
<source xml:lang="en">022 - Fatal Error: Table Integrity Suspect</source>
</trans-unit>
<trans-unit id="alertDialog.Severity023">
<source xml:lang="en">023 - Fatal Error: Database Integrity Suspect</source>
</trans-unit>
<trans-unit id="alertDialog.Severity024">
<source xml:lang="en">024 - Fatal Error: Hardware Error</source>
</trans-unit>
<trans-unit id="alertDialog.Severity025">
<source xml:lang="en">025 - Fatal Error</source>
</trans-unit>
<trans-unit id="alertDialog.AllDatabases">
<source xml:lang="en">&lt;all databases&gt;</source>
</trans-unit>
<trans-unit id="alertDialog.ExecuteJob">
<source xml:lang="en">Execute Job</source>
</trans-unit>
<trans-unit id="alertDialog.ExecuteJobName">
<source xml:lang="en">Job Name</source>
</trans-unit>
<trans-unit id="alertDialog.NotifyOperators">
<source xml:lang="en">Notify Operators</source>
</trans-unit>
<trans-unit id="alertDialog.NewJob">
<source xml:lang="en">New Job</source>
</trans-unit>
<trans-unit id="alertDialog.OperatorList">
<source xml:lang="en">Operator List</source>
</trans-unit>
<trans-unit id="alertDialog.OperatorName">
<source xml:lang="en">Operator</source>
</trans-unit>
<trans-unit id="alertDialog.OperatorEmail">
<source xml:lang="en">E-mail</source>
</trans-unit>
<trans-unit id="alertDialog.OperatorPager">
<source xml:lang="en">Pager</source>
</trans-unit>
<trans-unit id="alertDialog.NewOperator">
<source xml:lang="en">New Operator</source>
</trans-unit>
<trans-unit id="alertDialog.IncludeErrorInEmail">
<source xml:lang="en">Include alert error text in e-mail</source>
</trans-unit>
<trans-unit id="alertDialog.IncludeErrorInPager">
<source xml:lang="en">Include alert error text in pager</source>
</trans-unit>
<trans-unit id="alertDialog.AdditionalNotification">
<source xml:lang="en">Additional notification message to send</source>
</trans-unit>
<trans-unit id="alertDialog.DelayBetweenResponse">
<source xml:lang="en">Delay between responses</source>
</trans-unit>
<trans-unit id="alertDialog.DelayMinutes">
<source xml:lang="en">Delay Minutes</source>
</trans-unit>
<trans-unit id="alertDialog.DelaySeconds">
<source xml:lang="en">Delay Seconds</source>
</trans-unit>
</body></file>
<file original="extensions/agent/out/dialogs/agentDialog" source-language="en" datatype="plaintext"><body>
<trans-unit id="agentDialog.OK">
<source xml:lang="en">OK</source>
</trans-unit>
<trans-unit id="agentDialog.Cancel">
<source xml:lang="en">Cancel</source>
</trans-unit>
</body></file>
<file original="extensions/agent/out/data/jobData" source-language="en" datatype="plaintext"><body>
<trans-unit id="jobData.whenJobCompletes">
<source xml:lang="en">When the job completes</source>
</trans-unit>
<trans-unit id="jobData.whenJobFails">
<source xml:lang="en">When the job fails</source>
</trans-unit>
<trans-unit id="jobData.whenJobSucceeds">
<source xml:lang="en">When the job succeeds</source>
</trans-unit>
<trans-unit id="jobData.jobNameRequired">
<source xml:lang="en">Job name must be provided</source>
</trans-unit>
<trans-unit id="jobData.saveErrorMessage">
<source xml:lang="en">Job update failed '{0}'</source>
</trans-unit>
</body></file>
<file original="extensions/agent/out/data/alertData" source-language="en" datatype="plaintext"><body>
<trans-unit id="alertData.saveErrorMessage">
<source xml:lang="en">Alert update failed '{0}'</source>
</trans-unit>
<trans-unit id="alertData.DefaultAlertTypString">
<source xml:lang="en">SQL Server event alert</source>
</trans-unit>
<trans-unit id="alertDialog.PerformanceCondition">
<source xml:lang="en">SQL Server performance condition alert</source>
</trans-unit>
<trans-unit id="alertDialog.WmiEvent">
<source xml:lang="en">WMI event alert</source>
</trans-unit>
</body></file>
<file original="extensions/agent/out/mainController" source-language="en" datatype="plaintext"><body>
<trans-unit id="mainController.notImplemented">
<source xml:lang="en">This feature is under development. Check-out the latest insiders build if you'd like to try out the most recent changes!</source>
</trans-unit>
</body></file>
</xliff>

View File

@@ -0,0 +1,140 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file original="extensions/import/out/wizard/pages/summaryPage" source-language="en" datatype="plaintext"><body>
<trans-unit id="flatFileImport.importInformation">
<source xml:lang="en">Import information</source>
</trans-unit>
<trans-unit id="flatFileImport.importStatus">
<source xml:lang="en">Import status</source>
</trans-unit>
<trans-unit id="flatFileImport.serverName">
<source xml:lang="en">Server name</source>
</trans-unit>
<trans-unit id="flatFileImport.databaseName">
<source xml:lang="en">Database name</source>
</trans-unit>
<trans-unit id="flatFileImport.tableName">
<source xml:lang="en">Table name</source>
</trans-unit>
<trans-unit id="flatFileImport.tableSchema">
<source xml:lang="en">Table schema</source>
</trans-unit>
<trans-unit id="flatFileImport.fileImport">
<source xml:lang="en">File to be imported</source>
</trans-unit>
<trans-unit id="flatFileImport.success.norows">
<source xml:lang="en">✔ You have successfully inserted the data into a table.</source>
</trans-unit>
</body></file>
<file original="extensions/import/out/wizard/pages/prosePreviewPage" source-language="en" datatype="plaintext"><body>
<trans-unit id="flatFileImport.refresh">
<source xml:lang="en">Refresh</source>
</trans-unit>
<trans-unit id="flatFileImport.prosePreviewMessage">
<source xml:lang="en">This operation analyzed the input file structure to generate the preview below for up to the first 50 rows.</source>
</trans-unit>
</body></file>
<file original="extensions/import/out/wizard/pages/modifyColumnsPage" source-language="en" datatype="plaintext"><body>
<trans-unit id="flatFileImport.importData">
<source xml:lang="en">Import Data</source>
</trans-unit>
<trans-unit id="flatFileImport.next">
<source xml:lang="en">Next</source>
</trans-unit>
<trans-unit id="flatFileImport.columnName">
<source xml:lang="en">Column Name</source>
</trans-unit>
<trans-unit id="flatFileImport.dataType">
<source xml:lang="en">Data Type</source>
</trans-unit>
<trans-unit id="flatFileImport.primaryKey">
<source xml:lang="en">Primary Key</source>
</trans-unit>
<trans-unit id="flatFileImport.allowNulls">
<source xml:lang="en">Allow Nulls</source>
</trans-unit>
</body></file>
<file original="extensions/import/out/wizard/pages/fileConfigPage" source-language="en" datatype="plaintext"><body>
<trans-unit id="flatFileImport.serverDropdownTitle">
<source xml:lang="en">Server the database is in</source>
</trans-unit>
<trans-unit id="flatFileImport.databaseDropdownTitle">
<source xml:lang="en">Database the table is created in</source>
</trans-unit>
<trans-unit id="flatFileImport.browseFiles">
<source xml:lang="en">Browse</source>
</trans-unit>
<trans-unit id="flatFileImport.openFile">
<source xml:lang="en">Open</source>
</trans-unit>
<trans-unit id="flatFileImport.fileTextboxTitle">
<source xml:lang="en">Location of the file to be imported</source>
</trans-unit>
<trans-unit id="flatFileImport.tableTextboxTitle">
<source xml:lang="en">New table name</source>
</trans-unit>
<trans-unit id="flatFileImport.schemaTextboxTitle">
<source xml:lang="en">Table schema</source>
</trans-unit>
</body></file>
<file original="extensions/import/out/services/telemetry" source-language="en" datatype="plaintext"><body>
<trans-unit id="import.serviceCrashButton">
<source xml:lang="en">Give Feedback</source>
</trans-unit>
<trans-unit id="serviceCrashMessage">
<source xml:lang="en">service component could not start</source>
</trans-unit>
</body></file>
<file original="extensions/import/out/services/serviceClient" source-language="en" datatype="plaintext"><body>
<trans-unit id="serviceStarted">
<source xml:lang="en">Service Started</source>
</trans-unit>
<trans-unit id="serviceStarting">
<source xml:lang="en">Starting service</source>
</trans-unit>
<trans-unit id="flatFileImport.serviceStartFailed">
<source xml:lang="en">Failed to start Import service{0}</source>
</trans-unit>
<trans-unit id="installingServiceDetailed">
<source xml:lang="en">Installing {0} service to {1}</source>
</trans-unit>
<trans-unit id="installingService">
<source xml:lang="en">Installing Service</source>
</trans-unit>
<trans-unit id="serviceInstalled">
<source xml:lang="en">Installed</source>
</trans-unit>
<trans-unit id="downloadingService">
<source xml:lang="en">Downloading {0}</source>
</trans-unit>
<trans-unit id="downloadingServiceStatus">
<source xml:lang="en">Downloading Service</source>
</trans-unit>
<trans-unit id="downloadingServiceComplete">
<source xml:lang="en">Done!</source>
</trans-unit>
</body></file>
<file original="extensions/import/out/wizard/flatFileWizard" source-language="en" datatype="plaintext"><body>
<trans-unit id="import.needConnection">
<source xml:lang="en">Please connect to a server before using this wizard.</source>
</trans-unit>
<trans-unit id="flatFileImport.wizardName">
<source xml:lang="en">Import flat file wizard</source>
</trans-unit>
<trans-unit id="flatFileImport.page1Name">
<source xml:lang="en">Specify Input File</source>
</trans-unit>
<trans-unit id="flatFileImport.page2Name">
<source xml:lang="en">Preview Data</source>
</trans-unit>
<trans-unit id="flatFileImport.page3Name">
<source xml:lang="en">Modify Columns</source>
</trans-unit>
<trans-unit id="flatFileImport.page4Name">
<source xml:lang="en">Summary</source>
</trans-unit>
<trans-unit id="flatFileImport.importNewFile">
<source xml:lang="en">Import new file</source>
</trans-unit>
</body></file>
</xliff>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file original="extensions/profiler/client\out/dialogs/profilerCreateSessionDialog" source-language="en" datatype="plaintext"><body>
<trans-unit id="createSessionDialog.newSession">
<source xml:lang="en">New Session</source>
</trans-unit>
<trans-unit id="createSessionDialog.cancel">
<source xml:lang="en">Cancel</source>
</trans-unit>
<trans-unit id="createSessionDialog.create">
<source xml:lang="en">Create</source>
</trans-unit>
<trans-unit id="createSessionDialog.title">
<source xml:lang="en">Create New Profiler Session</source>
</trans-unit>
<trans-unit id="createSessionDialog.templatesInvalid">
<source xml:lang="en">Invalid templates list, cannot open dialog</source>
</trans-unit>
<trans-unit id="createSessionDialog.dialogOwnerInvalid">
<source xml:lang="en">Invalid dialog owner, cannot open dialog</source>
</trans-unit>
<trans-unit id="createSessionDialog.selectTemplates">
<source xml:lang="en">Select session template:</source>
</trans-unit>
<trans-unit id="createSessionDialog.enterSessionName">
<source xml:lang="en">Enter session name:</source>
</trans-unit>
</body></file>
</xliff>

File diff suppressed because it is too large Load Diff

View File

@@ -172,8 +172,7 @@
"dependencies": {
"fs-extra": "^5.0.0",
"handlebars": "^4.0.11",
"vscode-nls": "2.0.2",
"sqlops": "github:anthonydresser/sqlops-extension-sqlops"
"vscode-nls": "2.0.2"
},
"devDependencies": {
"@types/handlebars": "^4.0.11",
@@ -192,6 +191,7 @@
"typemoq": "^2.1.0",
"typescript": "^2.6.1",
"vscode": "^1.1.6",
"sqlops": "github:anthonydresser/sqlops-extension-sqlops",
"vsce": "1.36.2"
}
}

View File

@@ -20,7 +20,7 @@ export class Checkbox implements OnInit, OnChanges {
@Input() label: string;
@Input() enabled = true;
@Input() checked = true;
@Input() private ariaLabel: string;
@Input('aria-label') private ariaLabel: string;
@Output() onChange = new EventEmitter<boolean>();

View File

@@ -15,7 +15,6 @@ import { AngularDisposable } from 'sql/base/common/lifecycle';
import { attachInputBoxStyler } from 'vs/platform/theme/common/styler';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
@Component({
@@ -29,7 +28,7 @@ export class InputBox extends AngularDisposable implements OnInit, OnChanges {
@Input() max: string;
@Input() type: string;
@Input() placeholder: string;
@Input() ariaLabel: string;
@Input('aria-label') ariaLabel: string;
@Input() value: string;
@Output() onDidChange = new EventEmitter<string | number>();

View File

@@ -135,7 +135,7 @@ export abstract class Modal extends Disposable implements IThemable {
private _name: string,
private _partService: IPartService,
private _telemetryService: ITelemetryService,
private _contextKeyService: IContextKeyService,
_contextKeyService: IContextKeyService,
options?: IModalOptions
) {
super();

View File

@@ -49,7 +49,7 @@ export function createOptionElement(option: sqlops.ServiceOption, rowContainer:
optionWidget.value = optionValue;
inputElement = findElement(rowContainer, 'input');
} else if (option.valueType === ServiceOptionType.category || option.valueType === ServiceOptionType.boolean) {
optionWidget = new SelectBox(possibleInputs, optionValue.toString(), contextViewService);
optionWidget = new SelectBox(possibleInputs, optionValue.toString(), contextViewService, undefined, { ariaLabel: option.displayName });
DialogHelper.appendInputSelectBox(rowContainer, optionWidget);
inputElement = findElement(rowContainer, 'monaco-select-box');
} else if (option.valueType === ServiceOptionType.string || option.valueType === ServiceOptionType.password) {

View File

@@ -4,11 +4,9 @@
*--------------------------------------------------------------------------------------------*/
import { IThemable } from 'vs/platform/theme/common/styler';
import * as objects from 'sql/base/common/objects';
import { Event, Emitter } from 'vs/base/common/event';
import { Dimension } from 'vs/base/browser/dom';
import { Dimension, EventType } from 'vs/base/browser/dom';
import { $, Builder } from 'vs/base/browser/builder';
import { EventType } from 'vs/base/browser/dom';
import { IAction } from 'vs/base/common/actions';
import { IActionOptions, ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
@@ -17,12 +15,16 @@ import './panelStyles';
import { Disposable } from 'vs/base/common/lifecycle';
export interface IPanelStyles {
}
export interface IPanelOptions {
showHeaderWhenSingleView?: boolean;
}
export interface IPanelView {
render(container: HTMLElement): void;
layout(dimension: Dimension): void;
remove?(): void;
}
export interface IPanelTab {
@@ -36,6 +38,10 @@ interface IInternalPanelTab extends IPanelTab {
label: Builder;
}
const defaultOptions: IPanelOptions = {
showHeaderWhenSingleView: true
};
export type PanelTabIdentifier = string;
export class TabbedPanel extends Disposable implements IThemable {
@@ -49,11 +55,12 @@ export class TabbedPanel extends Disposable implements IThemable {
private _actionbar: ActionBar;
private _currentDimensions: Dimension;
private _collapsed = false;
private _headerVisible: boolean;
private _onTabChange = new Emitter<PanelTabIdentifier>();
public onTabChange: Event<PanelTabIdentifier> = this._onTabChange.event;
constructor(private container: HTMLElement) {
constructor(private container: HTMLElement, private options: IPanelOptions = defaultOptions) {
super();
this.$parent = this._register($('.tabbedPanel'));
this.$parent.appendTo(container);
@@ -65,7 +72,12 @@ export class TabbedPanel extends Disposable implements IThemable {
let actionbarcontainer = $('.title-actions');
this._actionbar = new ActionBar(actionbarcontainer.getHTMLElement());
this.$header.append(actionbarcontainer);
this.$parent.append(this.$header);
if (options.showHeaderWhenSingleView) {
this._headerVisible = true;
this.$parent.append(this.$header);
} else {
this._headerVisible = false;
}
this.$body = $('tabBody');
this.$body.attr('role', 'tabpanel');
this.$body.attr('tabindex', '0');
@@ -73,12 +85,16 @@ export class TabbedPanel extends Disposable implements IThemable {
}
public pushTab(tab: IPanelTab): PanelTabIdentifier {
let internalTab = objects.clone(tab) as IInternalPanelTab;
let internalTab = tab as IInternalPanelTab;
this._tabMap.set(tab.identifier, internalTab);
this._createTab(internalTab);
if (!this._shownTab) {
this.showTab(tab.identifier);
}
if (this._tabMap.size > 1 && !this._headerVisible) {
this.$parent.append(this.$header, 0);
this._headerVisible = true;
}
return tab.identifier as PanelTabIdentifier;
}
@@ -139,6 +155,11 @@ export class TabbedPanel extends Disposable implements IThemable {
}
public removeTab(tab: PanelTabIdentifier) {
let actualTab = this._tabMap.get(tab);
actualTab.header.destroy();
if (actualTab.view.remove) {
actualTab.view.remove();
}
this._tabMap.get(tab).header.destroy();
this._tabMap.delete(tab);
}
@@ -151,8 +172,9 @@ export class TabbedPanel extends Disposable implements IThemable {
this._currentDimensions = dimension;
this.$header.style('width', dimension.width + 'px');
this.$body.style('width', dimension.width + 'px');
this.$body.style('height', (dimension.height - this.headersize) + 'px');
this._layoutCurrentTab(new Dimension(dimension.width, dimension.height - this.headersize));
const bodyHeight = dimension.height - (this._headerVisible ? this.headersize : 0);
this.$body.style('height', bodyHeight + 'px');
this._layoutCurrentTab(new Dimension(dimension.width, bodyHeight));
}
private _layoutCurrentTab(dimension: Dimension): void {

View File

@@ -0,0 +1,212 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { INextIterator } from 'vs/base/common/iterator';
export interface IView {
id: string;
}
export interface IViewItem {
view: IView;
top: number;
height: number;
width: number;
}
export class HeightMap {
private heightMap: IViewItem[];
private indexes: { [item: string]: number; };
constructor() {
this.heightMap = [];
this.indexes = {};
}
public getContentHeight(): number {
let last = this.heightMap[this.heightMap.length - 1];
return !last ? 0 : last.top + last.height;
}
public onInsertItems(iterator: INextIterator<IViewItem>, afterItemId: string = null): number {
let viewItem: IViewItem;
let i: number, j: number;
let totalSize: number;
let sizeDiff = 0;
if (afterItemId === null) {
i = 0;
totalSize = 0;
} else {
i = this.indexes[afterItemId] + 1;
viewItem = this.heightMap[i - 1];
if (!viewItem) {
console.error('view item doesnt exist');
return undefined;
}
totalSize = viewItem.top + viewItem.height;
}
let boundSplice = this.heightMap.splice.bind(this.heightMap, i, 0);
let itemsToInsert: IViewItem[] = [];
while (viewItem = iterator.next()) {
viewItem.top = totalSize + sizeDiff;
this.indexes[viewItem.view.id] = i++;
itemsToInsert.push(viewItem);
sizeDiff += viewItem.height;
}
boundSplice.apply(this.heightMap, itemsToInsert);
for (j = i; j < this.heightMap.length; j++) {
viewItem = this.heightMap[j];
viewItem.top += sizeDiff;
this.indexes[viewItem.view.id] = j;
}
for (j = itemsToInsert.length - 1; j >= 0; j--) {
this.onInsertItem(itemsToInsert[j]);
}
for (j = this.heightMap.length - 1; j >= i; j--) {
this.onRefreshItem(this.heightMap[j]);
}
return sizeDiff;
}
public onInsertItem(item: IViewItem): void {
// noop
}
// Contiguous items
public onRemoveItems(iterator: INextIterator<string>): void {
let itemId: string;
let viewItem: IViewItem;
let startIndex: number = null;
let i: number;
let sizeDiff = 0;
while (itemId = iterator.next()) {
i = this.indexes[itemId];
viewItem = this.heightMap[i];
if (!viewItem) {
console.error('view item doesnt exist');
return;
}
sizeDiff -= viewItem.height;
delete this.indexes[itemId];
this.onRemoveItem(viewItem);
if (startIndex === null) {
startIndex = i;
}
}
if (sizeDiff === 0) {
return;
}
this.heightMap.splice(startIndex, i - startIndex + 1);
for (i = startIndex; i < this.heightMap.length; i++) {
viewItem = this.heightMap[i];
viewItem.top += sizeDiff;
this.indexes[viewItem.view.id] = i;
this.onRefreshItem(viewItem);
}
}
public onRemoveItem(item: IViewItem): void {
// noop
}
public onRefreshItem(item: IViewItem, needsRender: boolean = false): void {
// noop
}
protected updateSize(item: string, size: number): void {
let i = this.indexes[item];
let viewItem = this.heightMap[i];
viewItem.height = size;
}
protected updateTop(item: string, top: number): void {
let i = this.indexes[item];
let viewItem = this.heightMap[i];
viewItem.top = top;
}
public itemsCount(): number {
return this.heightMap.length;
}
public itemAt(position: number): string {
return this.heightMap[this.indexAt(position)].view.id;
}
public withItemsInRange(start: number, end: number, fn: (item: string) => void): void {
start = this.indexAt(start);
end = this.indexAt(end);
for (let i = start; i <= end; i++) {
fn(this.heightMap[i].view.id);
}
}
public indexAt(position: number): number {
let left = 0;
let right = this.heightMap.length;
let center: number;
let item: IViewItem;
// Binary search
while (left < right) {
center = Math.floor((left + right) / 2);
item = this.heightMap[center];
if (position < item.top) {
right = center;
} else if (position >= item.top + item.height) {
if (left === center) {
break;
}
left = center;
} else {
return center;
}
}
return this.heightMap.length;
}
public indexAfter(position: number): number {
return Math.min(this.indexAt(position) + 1, this.heightMap.length);
}
public itemAtIndex(index: number): IViewItem {
return this.heightMap[index];
}
public itemAfter(item: IViewItem): IViewItem {
return this.heightMap[this.indexes[item.view.id] + 1] || null;
}
public dispose(): void {
this.heightMap = null;
this.indexes = null;
}
}

View File

@@ -0,0 +1,8 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-scroll-split-view {
position: relative;
}

View File

@@ -0,0 +1,622 @@
/*---------------------------------------------------------------------------------------------
* 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 'vs/css!./scrollableSplitview';
import { IDisposable, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { mapEvent, Emitter, Event, debounceEvent } from 'vs/base/common/event';
import * as types from 'vs/base/common/types';
import * as dom from 'vs/base/browser/dom';
import { clamp } from 'vs/base/common/numbers';
import { range, firstIndex } from 'vs/base/common/arrays';
import { Sash, Orientation, ISashEvent as IBaseSashEvent } from 'vs/base/browser/ui/sash/sash';
import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
import { HeightMap, IView as HeightIView, IViewItem as HeightIViewItem } from './heightMap';
import { ArrayIterator } from 'vs/base/common/iterator';
import { mixin } from 'vs/base/common/objects';
export { Orientation } from 'vs/base/browser/ui/sash/sash';
export interface ISplitViewOptions {
orientation?: Orientation; // default Orientation.VERTICAL
enableResizing?: boolean;
}
const defaultOptions: ISplitViewOptions = {
enableResizing: true
};
export interface IView extends HeightIView {
readonly minimumSize: number;
readonly maximumSize: number;
readonly onDidChange: Event<number | undefined>;
render(container: HTMLElement, orientation: Orientation): void;
layout(size: number, orientation: Orientation): void;
}
interface ISashEvent {
sash: Sash;
start: number;
current: number;
}
interface IViewItem extends HeightIViewItem {
view: IView;
size: number;
container: HTMLElement;
disposable: IDisposable;
layout(): void;
}
interface ISashItem {
sash: Sash;
disposable: IDisposable;
}
interface ISashDragState {
index: number;
start: number;
sizes: number[];
}
enum State {
Idle,
Busy
}
function pushToEnd<T>(arr: T[], value: T): T[] {
let didFindValue = false;
const result = arr.filter(v => {
if (v === value) {
didFindValue = true;
return false;
}
return true;
});
if (didFindValue) {
result.push(value);
}
return result;
}
export class ScrollableSplitView extends HeightMap implements IDisposable {
private orientation: Orientation;
private el: HTMLElement;
private size = 0;
private contentSize = 0;
private viewItems: IViewItem[] = [];
private sashItems: ISashItem[] = [];
private sashDragState: ISashDragState;
private state: State = State.Idle;
private scrollable: ScrollableElement;
private options: ISplitViewOptions;
private dirtyState = false;
private lastRenderTop: number;
private lastRenderHeight: number;
private _onDidSashChange = new Emitter<void>();
readonly onDidSashChange = this._onDidSashChange.event;
private _onDidSashReset = new Emitter<void>();
readonly onDidSashReset = this._onDidSashReset.event;
get length(): number {
return this.viewItems.length;
}
constructor(container: HTMLElement, options: ISplitViewOptions = {}) {
super();
this.orientation = types.isUndefined(options.orientation) ? Orientation.VERTICAL : options.orientation;
this.options = mixin(options, defaultOptions, false);
this.el = document.createElement('div');
this.scrollable = new ScrollableElement(this.el, {});
debounceEvent(this.scrollable.onScroll, (l, e) => e, 25)(e => {
this.render(e.scrollTop, e.height);
this.relayout();
});
let domNode = this.scrollable.getDomNode();
dom.addClass(this.el, 'monaco-scroll-split-view');
dom.addClass(domNode, 'monaco-split-view2');
dom.addClass(domNode, this.orientation === Orientation.VERTICAL ? 'vertical' : 'horizontal');
container.appendChild(domNode);
}
addViews(views: IView[], sizes: number[], index = this.viewItems.length): void {
if (this.state !== State.Idle) {
throw new Error('Cant modify splitview');
}
this.state = State.Busy;
for (let i = 0; i < views.length; i++) {
let view = views[i], size = sizes[i];
// Add view
const container = dom.$('.split-view-view');
const onChangeDisposable = view.onDidChange(size => this.onViewChange(item, size));
const containerDisposable = toDisposable(() => {
if (container.parentElement) {
this.el.removeChild(container);
}
this.onRemoveItems(new ArrayIterator([item.view.id]));
});
const disposable = combinedDisposable([onChangeDisposable, containerDisposable]);
const layoutContainer = this.orientation === Orientation.VERTICAL
? size => item.container.style.height = `${item.size}px`
: size => item.container.style.width = `${item.size}px`;
const layout = () => {
layoutContainer(item.size);
item.view.layout(item.size, this.orientation);
};
size = Math.round(size);
const item: IViewItem = { view, container, size, layout, disposable, height: size, top: 0, width: 0 };
this.viewItems.splice(index, 0, item);
this.onInsertItems(new ArrayIterator([item]), index > 0 ? this.viewItems[index - 1].view.id : undefined);
// Add sash
if (this.options.enableResizing && this.viewItems.length > 1) {
const orientation = this.orientation === Orientation.VERTICAL ? Orientation.HORIZONTAL : Orientation.VERTICAL;
const layoutProvider = this.orientation === Orientation.VERTICAL ? { getHorizontalSashTop: sash => this.getSashPosition(sash) } : { getVerticalSashLeft: sash => this.getSashPosition(sash) };
const sash = new Sash(this.el, layoutProvider, { orientation });
const sashEventMapper = this.orientation === Orientation.VERTICAL
? (e: IBaseSashEvent) => ({ sash, start: e.startY, current: e.currentY })
: (e: IBaseSashEvent) => ({ sash, start: e.startX, current: e.currentX });
const onStart = mapEvent(sash.onDidStart, sashEventMapper);
const onStartDisposable = onStart(this.onSashStart, this);
const onChange = mapEvent(sash.onDidChange, sashEventMapper);
const onSashChangeDisposable = onChange(this.onSashChange, this);
const onEnd = mapEvent<void, void>(sash.onDidEnd, () => null);
const onEndDisposable = onEnd(() => this._onDidSashChange.fire());
const onDidReset = mapEvent<void, void>(sash.onDidReset, () => null);
const onDidResetDisposable = onDidReset(() => this._onDidSashReset.fire());
const disposable = combinedDisposable([onStartDisposable, onSashChangeDisposable, onEndDisposable, onDidResetDisposable, sash]);
const sashItem: ISashItem = { sash, disposable };
this.sashItems.splice(index - 1, 0, sashItem);
}
view.render(container, this.orientation);
}
this.relayout(index);
this.state = State.Idle;
}
addView(view: IView, size: number, index = this.viewItems.length): void {
if (this.state !== State.Idle) {
throw new Error('Cant modify splitview');
}
this.state = State.Busy;
// Add view
const container = dom.$('.split-view-view');
const onChangeDisposable = view.onDidChange(size => this.onViewChange(item, size));
const containerDisposable = toDisposable(() => {
if (container.parentElement) {
this.el.removeChild(container);
}
this.onRemoveItems(new ArrayIterator([item.view.id]));
});
const disposable = combinedDisposable([onChangeDisposable, containerDisposable]);
const layoutContainer = this.orientation === Orientation.VERTICAL
? size => item.container.style.height = `${item.size}px`
: size => item.container.style.width = `${item.size}px`;
const layout = () => {
layoutContainer(item.size);
item.view.layout(item.size, this.orientation);
};
size = Math.round(size);
const item: IViewItem = { view, container, size, layout, disposable, height: size, top: 0, width: 0 };
this.viewItems.splice(index, 0, item);
this.onInsertItems(new ArrayIterator([item]), index > 0 ? this.viewItems[index - 1].view.id : undefined);
// Add sash
if (this.options.enableResizing && this.viewItems.length > 1) {
const orientation = this.orientation === Orientation.VERTICAL ? Orientation.HORIZONTAL : Orientation.VERTICAL;
const layoutProvider = this.orientation === Orientation.VERTICAL ? { getHorizontalSashTop: sash => this.getSashPosition(sash) } : { getVerticalSashLeft: sash => this.getSashPosition(sash) };
const sash = new Sash(this.el, layoutProvider, { orientation });
const sashEventMapper = this.orientation === Orientation.VERTICAL
? (e: IBaseSashEvent) => ({ sash, start: e.startY, current: e.currentY })
: (e: IBaseSashEvent) => ({ sash, start: e.startX, current: e.currentX });
const onStart = mapEvent(sash.onDidStart, sashEventMapper);
const onStartDisposable = onStart(this.onSashStart, this);
const onChange = mapEvent(sash.onDidChange, sashEventMapper);
const onSashChangeDisposable = onChange(this.onSashChange, this);
const onEnd = mapEvent<void, void>(sash.onDidEnd, () => null);
const onEndDisposable = onEnd(() => this._onDidSashChange.fire());
const onDidReset = mapEvent<void, void>(sash.onDidReset, () => null);
const onDidResetDisposable = onDidReset(() => this._onDidSashReset.fire());
const disposable = combinedDisposable([onStartDisposable, onSashChangeDisposable, onEndDisposable, onDidResetDisposable, sash]);
const sashItem: ISashItem = { sash, disposable };
sash.hide();
this.sashItems.splice(index - 1, 0, sashItem);
}
view.render(container, this.orientation);
this.relayout(index);
this.state = State.Idle;
}
removeView(index: number): void {
if (this.state !== State.Idle) {
throw new Error('Cant modify splitview');
}
this.state = State.Busy;
if (index < 0 || index >= this.viewItems.length) {
return;
}
// Remove view
const viewItem = this.viewItems.splice(index, 1)[0];
viewItem.disposable.dispose();
// Remove sash
if (this.options.enableResizing && this.viewItems.length >= 1) {
const sashIndex = Math.max(index - 1, 0);
const sashItem = this.sashItems.splice(sashIndex, 1)[0];
sashItem.disposable.dispose();
} else {
this.lastRenderHeight = NaN, this.lastRenderTop = NaN;
}
this.relayout();
this.state = State.Idle;
}
moveView(from: number, to: number): void {
if (this.state !== State.Idle) {
throw new Error('Cant modify splitview');
}
this.state = State.Busy;
if (from < 0 || from >= this.viewItems.length) {
return;
}
if (to < 0 || to >= this.viewItems.length) {
return;
}
if (from === to) {
return;
}
const viewItem = this.viewItems.splice(from, 1)[0];
this.viewItems.splice(to, 0, viewItem);
if (to + 1 < this.viewItems.length) {
this.el.insertBefore(viewItem.container, this.viewItems[to + 1].container);
} else {
this.el.appendChild(viewItem.container);
}
this.layoutViews();
this.state = State.Idle;
}
private relayout(lowPriorityIndex?: number): void {
const contentSize = this.viewItems.reduce((r, i) => r + i.size, 0);
this.resize(this.viewItems.length - 1, this.size - contentSize, undefined, lowPriorityIndex);
}
layout(size: number): void {
const previousSize = Math.max(this.size, this.contentSize);
this.size = size;
this.resize(this.viewItems.length - 1, size - previousSize);
}
private render(scrollTop: number, viewHeight: number): void {
let i: number;
let stop: number;
let renderTop = scrollTop;
let renderBottom = scrollTop + viewHeight;
let thisRenderBottom = this.lastRenderTop + this.lastRenderHeight;
// when view scrolls down, start rendering from the renderBottom
for (i = this.indexAfter(renderBottom) - 1, stop = this.indexAt(Math.max(thisRenderBottom, renderTop)); i >= stop; i--) {
if (this.insertItemInDOM(<IViewItem>this.itemAtIndex(i))) {
this.dirtyState = true;
}
}
// when view scrolls up, start rendering from either this.renderTop or renderBottom
for (i = Math.min(this.indexAt(this.lastRenderTop), this.indexAfter(renderBottom)) - 1, stop = this.indexAt(renderTop); i >= stop; i--) {
if (this.insertItemInDOM(<IViewItem>this.itemAtIndex(i))) {
this.dirtyState = true;
}
}
// when view scrolls down, start unrendering from renderTop
for (i = this.indexAt(this.lastRenderTop), stop = Math.min(this.indexAt(renderTop), this.indexAfter(thisRenderBottom)); i < stop; i++) {
if (this.removeItemFromDOM(<IViewItem>this.itemAtIndex(i))) {
this.dirtyState = true;
}
}
// when view scrolls up, start unrendering from either renderBottom this.renderTop
for (i = Math.max(this.indexAfter(renderBottom), this.indexAt(this.lastRenderTop)), stop = this.indexAfter(thisRenderBottom); i < stop; i++) {
if (this.removeItemFromDOM(<IViewItem>this.itemAtIndex(i))) {
this.dirtyState = true;
}
}
let topItem = this.itemAtIndex(this.indexAt(renderTop));
if (topItem) {
this.el.style.top = (topItem.top - renderTop) + 'px';
}
this.lastRenderTop = renderTop;
this.lastRenderHeight = renderBottom - renderTop;
}
private onSashStart({ sash, start }: ISashEvent): void {
const index = firstIndex(this.sashItems, item => item.sash === sash);
const sizes = this.viewItems.map(i => i.size);
// const upIndexes = range(index, -1);
// const collapseUp = upIndexes.reduce((r, i) => r + (sizes[i] - this.viewItems[i].view.minimumSize), 0);
// const expandUp = upIndexes.reduce((r, i) => r + (this.viewItems[i].view.maximumSize - sizes[i]), 0);
// const downIndexes = range(index + 1, this.viewItems.length);
// const collapseDown = downIndexes.reduce((r, i) => r + (sizes[i] - this.viewItems[i].view.minimumSize), 0);
// const expandDown = downIndexes.reduce((r, i) => r + (this.viewItems[i].view.maximumSize - sizes[i]), 0);
// const minDelta = -Math.min(collapseUp, expandDown);
// const maxDelta = Math.min(collapseDown, expandUp);
this.sashDragState = { start, index, sizes };
}
private onSashChange({ sash, current }: ISashEvent): void {
const { index, start, sizes } = this.sashDragState;
const delta = current - start;
this.resize(index, delta, sizes);
}
private onViewChange(item: IViewItem, size: number | undefined): void {
const index = this.viewItems.indexOf(item);
if (index < 0 || index >= this.viewItems.length) {
return;
}
size = typeof size === 'number' ? size : item.size;
size = clamp(size, item.view.minimumSize, item.view.maximumSize);
item.size = size;
this.relayout(index);
}
resizeView(index: number, size: number): void {
if (this.state !== State.Idle) {
throw new Error('Cant modify splitview');
}
this.state = State.Busy;
if (index < 0 || index >= this.viewItems.length) {
return;
}
const item = this.viewItems[index];
size = Math.round(size);
size = clamp(size, item.view.minimumSize, item.view.maximumSize);
let delta = size - item.size;
if (delta !== 0 && index < this.viewItems.length - 1) {
const downIndexes = range(index + 1, this.viewItems.length);
const collapseDown = downIndexes.reduce((r, i) => r + (this.viewItems[i].size - this.viewItems[i].view.minimumSize), 0);
const expandDown = downIndexes.reduce((r, i) => r + (this.viewItems[i].view.maximumSize - this.viewItems[i].size), 0);
const deltaDown = clamp(delta, -expandDown, collapseDown);
this.resize(index, deltaDown);
delta -= deltaDown;
}
if (delta !== 0 && index > 0) {
const upIndexes = range(index - 1, -1);
const collapseUp = upIndexes.reduce((r, i) => r + (this.viewItems[i].size - this.viewItems[i].view.minimumSize), 0);
const expandUp = upIndexes.reduce((r, i) => r + (this.viewItems[i].view.maximumSize - this.viewItems[i].size), 0);
const deltaUp = clamp(-delta, -collapseUp, expandUp);
this.resize(index - 1, deltaUp);
}
this.state = State.Idle;
}
// DOM changes
private insertItemInDOM(item: IViewItem): boolean {
if (item.container.parentElement) {
return false;
}
let elementAfter: HTMLElement = null;
let itemAfter = <IViewItem>this.itemAfter(item);
if (itemAfter && itemAfter.container) {
elementAfter = itemAfter.container;
}
if (elementAfter === null) {
this.el.appendChild(item.container);
} else {
try {
this.el.insertBefore(item.container, elementAfter);
} catch (e) {
// console.warn('Failed to locate previous tree element');
this.el.appendChild(item.container);
}
}
item.layout();
return true;
}
private removeItemFromDOM(item: IViewItem): boolean {
if (!item || !item.container || !item.container.parentElement) {
return false;
}
this.el.removeChild(item.container);
return true;
}
getViewSize(index: number): number {
if (index < 0 || index >= this.viewItems.length) {
return -1;
}
return this.viewItems[index].size;
}
private resize(index: number, delta: number, sizes = this.viewItems.map(i => i.size), lowPriorityIndex?: number): void {
if (index < 0 || index >= this.viewItems.length) {
return;
}
if (delta !== 0) {
let upIndexes = range(index, -1);
let downIndexes = range(index + 1, this.viewItems.length);
if (typeof lowPriorityIndex === 'number') {
upIndexes = pushToEnd(upIndexes, lowPriorityIndex);
downIndexes = pushToEnd(downIndexes, lowPriorityIndex);
}
const upItems = upIndexes.map(i => this.viewItems[i]);
const upSizes = upIndexes.map(i => sizes[i]);
const downItems = downIndexes.map(i => this.viewItems[i]);
const downSizes = downIndexes.map(i => sizes[i]);
for (let i = 0, deltaUp = delta; deltaUp !== 0 && i < upItems.length; i++) {
const item = upItems[i];
const size = clamp(upSizes[i] + deltaUp, item.view.minimumSize, item.view.maximumSize);
const viewDelta = size - upSizes[i];
deltaUp -= viewDelta;
item.size = size;
}
for (let i = 0, deltaDown = delta; deltaDown !== 0 && i < downItems.length; i++) {
const item = downItems[i];
const size = clamp(downSizes[i] - deltaDown, item.view.minimumSize, item.view.maximumSize);
const viewDelta = size - downSizes[i];
deltaDown += viewDelta;
item.size = size;
}
}
let contentSize = this.viewItems.reduce((r, i) => r + i.size, 0);
let emptyDelta = this.size - contentSize;
for (let i = this.viewItems.length - 1; emptyDelta > 0 && i >= 0; i--) {
const item = this.viewItems[i];
const size = clamp(item.size + emptyDelta, item.view.minimumSize, item.view.maximumSize);
const viewDelta = size - item.size;
emptyDelta -= viewDelta;
item.size = size;
}
this.contentSize = this.viewItems.reduce((r, i) => r + i.size, 0);
this.scrollable.setScrollDimensions({
scrollHeight: this.contentSize,
height: this.size
});
this.layoutViews();
}
private layoutViews(): void {
if (this.dirtyState) {
for (let i = this.indexAt(this.lastRenderTop); i <= this.indexAfter(this.lastRenderTop + this.lastRenderHeight) - 1; i++) {
this.viewItems[i].layout();
if (this.options.enableResizing) {
this.sashItems[i].sash.layout();
}
}
this.dirtyState = false;
}
// Update sashes enablement
// let previous = false;
// const collapsesDown = this.viewItems.map(i => previous = (i.size - i.view.minimumSize > 0) || previous);
// previous = false;
// const expandsDown = this.viewItems.map(i => previous = (i.view.maximumSize - i.size > 0) || previous);
// const reverseViews = [...this.viewItems].reverse();
// previous = false;
// const collapsesUp = reverseViews.map(i => previous = (i.size - i.view.minimumSize > 0) || previous).reverse();
// previous = false;
// const expandsUp = reverseViews.map(i => previous = (i.view.maximumSize - i.size > 0) || previous).reverse();
// this.sashItems.forEach((s, i) => {
// if ((collapsesDown[i] && expandsUp[i + 1]) || (expandsDown[i] && collapsesUp[i + 1])) {
// s.sash.enable();
// } else {
// s.sash.disable();
// }
// });
}
private getSashPosition(sash: Sash): number {
let position = 0;
for (let i = 0; i < this.sashItems.length; i++) {
position += this.viewItems[i].size;
if (this.sashItems[i].sash === sash) {
return position;
}
}
return 0;
}
dispose(): void {
this.viewItems.forEach(i => i.disposable.dispose());
this.viewItems = [];
this.sashItems.forEach(i => i.disposable.dispose());
this.sashItems = [];
}
}

View File

@@ -28,6 +28,7 @@ export class SelectBox extends AngularDisposable implements OnInit, OnChanges {
@Input() options: string[];
@Input() selectedOption: string;
@Input() onlyEmitOnChange = false;
@Input('aria-label') ariaLabel: string;
@Output() onDidSelect = new EventEmitter<ISelectData>();
@@ -42,7 +43,7 @@ export class SelectBox extends AngularDisposable implements OnInit, OnChanges {
}
ngOnInit(): void {
this._selectbox = new vsSelectBox(this.options, this.selectedOption, this.contextViewService);
this._selectbox = new vsSelectBox(this.options, this.selectedOption, this.contextViewService, undefined, { ariaLabel: this.ariaLabel });
this._selectbox.render(this._el.nativeElement);
this._selectbox.onDidSelect(e => {
if (this.onlyEmitOnChange) {

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import { SelectBox as vsSelectBox, ISelectBoxStyles as vsISelectBoxStyles } from 'vs/base/browser/ui/selectBox/selectBox';
import { SelectBox as vsSelectBox, ISelectBoxStyles as vsISelectBoxStyles, ISelectBoxOptions } from 'vs/base/browser/ui/selectBox/selectBox';
import { Color } from 'vs/base/common/color';
import { IContextViewProvider, AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview';
import * as dom from 'vs/base/browser/dom';
@@ -46,8 +46,8 @@ export class SelectBox extends vsSelectBox {
private inputValidationErrorBackground: Color;
private element: HTMLElement;
constructor(options: string[], selectedOption: string, contextViewProvider: IContextViewProvider, container?: HTMLElement) {
super(options, 0, contextViewProvider);
constructor(options: string[], selectedOption: string, contextViewProvider: IContextViewProvider, container?: HTMLElement, selectBoxOptions?: ISelectBoxOptions) {
super(options, 0, contextViewProvider, undefined, selectBoxOptions);
this._optionsDictionary = new Array();
for (var i = 0; i < options.length; i++) {
this._optionsDictionary[options[i]] = i;

View File

@@ -0,0 +1,215 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export interface IObservableCollection<T> {
getLength(): number;
at(index: number): T;
getRange(start: number, end: number): T[];
setCollectionChangedCallback(callback: (change: CollectionChange, startIndex: number, count: number) => void): void;
}
export interface IGridDataRow {
row?: number;
values: any[];
}
export enum CollectionChange {
ItemsReplaced
}
class LoadCancellationToken {
isCancelled: boolean;
}
class DataWindow<TData> {
private _dataSourceLength: number;
private _data: TData[];
private _length: number = 0;
private _offsetFromDataSource: number = -1;
private loadFunction: (offset: number, count: number) => Thenable<TData[]>;
private lastLoadCancellationToken: LoadCancellationToken;
private loadCompleteCallback: (start: number, end: number) => void;
private placeholderItemGenerator: (index: number) => TData;
constructor(dataSourceLength: number,
loadFunction: (offset: number, count: number) => Thenable<TData[]>,
placeholderItemGenerator: (index: number) => TData,
loadCompleteCallback: (start: number, end: number) => void) {
this._dataSourceLength = dataSourceLength;
this.loadFunction = loadFunction;
this.placeholderItemGenerator = placeholderItemGenerator;
this.loadCompleteCallback = loadCompleteCallback;
}
getStartIndex(): number {
return this._offsetFromDataSource;
}
getEndIndex(): number {
return this._offsetFromDataSource + this._length;
}
contains(dataSourceIndex: number): boolean {
return dataSourceIndex >= this.getStartIndex() && dataSourceIndex < this.getEndIndex();
}
getItem(index: number): TData {
if (!this._data) {
return this.placeholderItemGenerator(index);
}
return this._data[index - this._offsetFromDataSource];
}
positionWindow(offset: number, length: number): void {
this._offsetFromDataSource = offset;
this._length = length;
this._data = undefined;
if (this.lastLoadCancellationToken) {
this.lastLoadCancellationToken.isCancelled = true;
}
if (length === 0) {
return;
}
let cancellationToken = new LoadCancellationToken();
this.lastLoadCancellationToken = cancellationToken;
this.loadFunction(offset, length).then(data => {
if (!cancellationToken.isCancelled) {
this._data = data;
this.loadCompleteCallback(this._offsetFromDataSource, this._offsetFromDataSource + this._length);
}
});
}
}
export class VirtualizedCollection<TData> implements IObservableCollection<TData> {
private _length: number;
private _windowSize: number;
private _bufferWindowBefore: DataWindow<TData>;
private _window: DataWindow<TData>;
private _bufferWindowAfter: DataWindow<TData>;
private collectionChangedCallback: (change: CollectionChange, startIndex: number, count: number) => void;
constructor(windowSize: number,
length: number,
loadFn: (offset: number, count: number) => Thenable<TData[]>,
private _placeHolderGenerator: (index: number) => TData) {
this._windowSize = windowSize;
this._length = length;
let loadCompleteCallback = (start: number, end: number) => {
if (this.collectionChangedCallback) {
this.collectionChangedCallback(CollectionChange.ItemsReplaced, start, end - start);
}
};
this._bufferWindowBefore = new DataWindow(length, loadFn, _placeHolderGenerator, loadCompleteCallback);
this._window = new DataWindow(length, loadFn, _placeHolderGenerator, loadCompleteCallback);
this._bufferWindowAfter = new DataWindow(length, loadFn, _placeHolderGenerator, loadCompleteCallback);
}
setCollectionChangedCallback(callback: (change: CollectionChange, startIndex: number, count: number) => void): void {
this.collectionChangedCallback = callback;
}
getLength(): number {
return this._length;
}
at(index: number): TData {
return this.getRange(index, index + 1)[0];
}
getRange(start: number, end: number): TData[] {
// current data may contain placeholders
let currentData = this.getRangeFromCurrent(start, end);
// only shift window and make promise of refreshed data in following condition:
if (start < this._bufferWindowBefore.getStartIndex() || end > this._bufferWindowAfter.getEndIndex()) {
// jump, reset
this.resetWindowsAroundIndex(start);
} else if (end <= this._bufferWindowBefore.getEndIndex()) {
// scroll up, shift up
let windowToRecycle = this._bufferWindowAfter;
this._bufferWindowAfter = this._window;
this._window = this._bufferWindowBefore;
this._bufferWindowBefore = windowToRecycle;
let newWindowOffset = Math.max(0, this._window.getStartIndex() - this._windowSize);
this._bufferWindowBefore.positionWindow(newWindowOffset, this._window.getStartIndex() - newWindowOffset);
} else if (start >= this._bufferWindowAfter.getStartIndex()) {
// scroll down, shift down
let windowToRecycle = this._bufferWindowBefore;
this._bufferWindowBefore = this._window;
this._window = this._bufferWindowAfter;
this._bufferWindowAfter = windowToRecycle;
let newWindowOffset = Math.min(this._window.getStartIndex() + this._windowSize, this._length);
let newWindowLength = Math.min(this._length - newWindowOffset, this._windowSize);
this._bufferWindowAfter.positionWindow(newWindowOffset, newWindowLength);
}
return currentData;
}
private getRangeFromCurrent(start: number, end: number): TData[] {
let currentData = [];
for (let i = 0; i < end - start; i++) {
currentData.push(this.getDataFromCurrent(start + i));
}
return currentData;
}
private getDataFromCurrent(index: number): TData {
if (this._bufferWindowBefore.contains(index)) {
return this._bufferWindowBefore.getItem(index);
} else if (this._bufferWindowAfter.contains(index)) {
return this._bufferWindowAfter.getItem(index);
} else if (this._window.contains(index)) {
return this._window.getItem(index);
}
return this._placeHolderGenerator(index);
}
private resetWindowsAroundIndex(index: number): void {
let bufferWindowBeforeStart = Math.max(0, index - this._windowSize * 1.5);
let bufferWindowBeforeEnd = Math.max(0, index - this._windowSize / 2);
this._bufferWindowBefore.positionWindow(bufferWindowBeforeStart, bufferWindowBeforeEnd - bufferWindowBeforeStart);
let mainWindowStart = bufferWindowBeforeEnd;
let mainWindowEnd = Math.min(mainWindowStart + this._windowSize, this._length);
this._window.positionWindow(mainWindowStart, mainWindowEnd - mainWindowStart);
let bufferWindowAfterStart = mainWindowEnd;
let bufferWindowAfterEnd = Math.min(bufferWindowAfterStart + this._windowSize, this._length);
this._bufferWindowAfter.positionWindow(bufferWindowAfterStart, bufferWindowAfterEnd - bufferWindowAfterStart);
}
}
export class AsyncDataProvider<TData extends IGridDataRow> implements Slick.DataProvider<TData> {
constructor(private dataRows: IObservableCollection<TData>) { }
public getLength(): number {
return this.dataRows ? this.dataRows.getLength() : 0;
}
public getItem(index: number): TData {
return !this.dataRows ? undefined : this.dataRows.at(index);
}
public getRange(start: number, end: number): TData[] {
return !this.dataRows ? undefined : this.dataRows.getRange(start, end);
}
}

View File

@@ -46,7 +46,8 @@
background-image: url('down.svg');
}
.vs-dark .slick-header-menubutton {
.vs-dark .slick-header-menubutton,
.hc-black .slick-header-menubutton {
background-image: url('down-inverse.svg');
}
@@ -54,7 +55,8 @@
background-image: url('filter.svg');
}
.vs-dark .slick-header-menubutton.filtered {
.vs-dark .slick-header-menubutton.filtered,
.hc-black .slick-header-menubutton.filtered {
background-image: url('filter_inverse.svg');
}
@@ -74,6 +76,10 @@
background: none repeat scroll 0 0 #333333;
}
.hc-black .slick-header-menu {
background: none repeat scroll 0 0 #000000;
}
.slick-header-menu a.monaco-button.monaco-text-button {
width: 60px;
margin: 6px 6px 6px 6px;
@@ -144,4 +150,8 @@ label {
.vs-dark .slick-header-menu > input.input {
color: #4a4a4a;
}
.hc-black .slick-header-menu > input.input {
color: #000000;
}

View File

@@ -0,0 +1,76 @@
/*---------------------------------------------------------------------------------------------
* 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 * as DOM from 'vs/base/browser/dom';
import { StandardMouseWheelEvent } from 'vs/base/browser/mouseEvent';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { mixin } from 'vs/base/common/objects';
const SCROLL_WHEEL_SENSITIVITY = 50;
export interface IMouseWheelSupportOptions {
scrollSpeed?: number;
}
const defaultOptions: IMouseWheelSupportOptions = {
scrollSpeed: SCROLL_WHEEL_SENSITIVITY
};
export class MouseWheelSupport implements Slick.Plugin<any> {
private viewport: HTMLElement;
private canvas: HTMLElement;
private options: IMouseWheelSupportOptions;
private _disposables: IDisposable[] = [];
constructor(options: IMouseWheelSupportOptions = {}) {
this.options = mixin(options, defaultOptions);
}
public init(grid: Slick.Grid<any>): void {
this.canvas = grid.getCanvasNode();
this.viewport = this.canvas.parentElement;
let onMouseWheel = (browserEvent: MouseWheelEvent) => {
let e = new StandardMouseWheelEvent(browserEvent);
this._onMouseWheel(e);
};
this._disposables.push(DOM.addDisposableListener(this.viewport, 'mousewheel', onMouseWheel));
this._disposables.push(DOM.addDisposableListener(this.viewport, 'DOMMouseScroll', onMouseWheel));
}
private _onMouseWheel(event: StandardMouseWheelEvent) {
const scrollHeight = this.canvas.clientHeight;
const height = this.viewport.clientHeight;
const scrollDown = Math.sign(event.deltaY) === -1;
if (scrollDown) {
if ((this.viewport.scrollTop - (event.deltaY * this.options.scrollSpeed)) + height > scrollHeight) {
this.viewport.scrollTop = scrollHeight - height;
this.viewport.dispatchEvent(new Event('scroll'));
} else {
this.viewport.scrollTop = this.viewport.scrollTop - (event.deltaY * this.options.scrollSpeed);
this.viewport.dispatchEvent(new Event('scroll'));
event.stopPropagation();
event.preventDefault();
}
} else {
if ((this.viewport.scrollTop - (event.deltaY * this.options.scrollSpeed)) < 0) {
this.viewport.scrollTop = 0;
this.viewport.dispatchEvent(new Event('scroll'));
} else {
this.viewport.scrollTop = this.viewport.scrollTop - (event.deltaY * this.options.scrollSpeed);
this.viewport.dispatchEvent(new Event('scroll'));
event.stopPropagation();
event.preventDefault();
}
}
}
destroy() {
dispose(this._disposables);
}
}

View File

@@ -11,9 +11,17 @@ import { IListStyles } from 'vs/base/browser/ui/list/listWidget';
import * as DOM from 'vs/base/browser/dom';
import { Color } from 'vs/base/common/color';
import { mixin } from 'vs/base/common/objects';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { Orientation } from 'vs/base/browser/ui/splitview/splitview';
import { Widget } from 'vs/base/browser/ui/widget';
import { isArray, isBoolean } from 'vs/base/common/types';
import { Event, Emitter } from 'vs/base/common/event';
import { range } from 'vs/base/common/arrays';
export interface ITableContextMenuEvent {
anchor: HTMLElement | { x: number, y: number };
cell?: { row: number, cell: number };
}
export interface ITableStyles extends IListStyles {
tableHeaderBackground?: Color;
@@ -27,30 +35,46 @@ function getDefaultOptions<T>(): Slick.GridOptions<T> {
};
}
export class Table<T extends Slick.SlickData> extends Widget implements IThemable {
export interface ITableSorter<T> {
sort(args: Slick.OnSortEventArgs<T>);
}
export interface ITableConfiguration<T> {
dataProvider?: Slick.DataProvider<T> | Array<T>;
columns?: Slick.Column<T>[];
sorter?: ITableSorter<T>;
}
export class Table<T extends Slick.SlickData> extends Widget implements IThemable, IDisposable {
private styleElement: HTMLStyleElement;
private idPrefix: string;
private _grid: Slick.Grid<T>;
private _columns: Slick.Column<T>[];
private _data: TableDataView<T>;
private _data: Slick.DataProvider<T>;
private _sorter: ITableSorter<T>;
private _autoscroll: boolean;
private _onRowCountChangeListener: IDisposable;
private _container: HTMLElement;
private _tableContainer: HTMLElement;
private _classChangeTimeout: number;
constructor(parent: HTMLElement, data?: Array<T> | TableDataView<T>, columns?: Slick.Column<T>[], options?: Slick.GridOptions<T>) {
private _disposables: IDisposable[] = [];
private _onContextMenu = new Emitter<ITableContextMenuEvent>();
public readonly onContextMenu: Event<ITableContextMenuEvent> = this._onContextMenu.event;
constructor(parent: HTMLElement, configuration?: ITableConfiguration<T>, options?: Slick.GridOptions<T>) {
super();
if (data instanceof TableDataView) {
this._data = data;
if (!configuration || isArray(configuration.dataProvider)) {
this._data = new TableDataView<T>(configuration && configuration.dataProvider as Array<T>);
} else {
this._data = new TableDataView<T>(data);
this._data = configuration.dataProvider;
}
if (columns) {
this._columns = columns;
if (configuration.columns) {
this._columns = configuration.columns;
} else {
this._columns = new Array<Slick.Column<T>>();
}
@@ -81,15 +105,33 @@ export class Table<T extends Slick.SlickData> extends Widget implements IThemabl
this._grid = new Slick.Grid<T>(this._tableContainer, this._data, this._columns, newOptions);
this.idPrefix = this._tableContainer.classList[0];
DOM.addClass(this._container, this.idPrefix);
this._onRowCountChangeListener = this._data.onRowCountChange(() => this._handleRowCountChange());
this._grid.onSort.subscribe((e, args) => {
this._data.sort(args);
this._grid.invalidate();
this._grid.render();
if (configuration.sorter) {
this._sorter = configuration.sorter;
this._grid.onSort.subscribe((e, args) => {
this._sorter.sort(args);
this._grid.invalidate();
this._grid.render();
});
}
this._grid.onContextMenu.subscribe((e: JQuery.Event) => {
const originalEvent = e.originalEvent;
const cell = this._grid.getCellFromEvent(originalEvent);
const anchor = originalEvent instanceof MouseEvent ? { x: originalEvent.x, y: originalEvent.y } : originalEvent.srcElement as HTMLElement;
this._onContextMenu.fire({ anchor, cell });
});
}
private _handleRowCountChange() {
public dispose() {
dispose(this._disposables);
}
public invalidateRows(rows: number[], keepEditor: boolean) {
this._grid.invalidateRows(rows, keepEditor);
this._grid.render();
}
public updateRowCount() {
this._grid.updateRowCount();
this._grid.render();
if (this._autoscroll) {
@@ -113,20 +155,22 @@ export class Table<T extends Slick.SlickData> extends Widget implements IThemabl
} else {
this._data = new TableDataView<T>(data);
}
this._onRowCountChangeListener.dispose();
this._grid.setData(this._data, true);
this._onRowCountChangeListener = this._data.onRowCountChange(() => this._handleRowCountChange());
}
get columns(): Slick.Column<T>[] {
return this._grid.getColumns();
}
setSelectedRows(rows: number[]) {
this._grid.setSelectedRows(rows);
public setSelectedRows(rows: number[] | boolean) {
if (isBoolean(rows)) {
this._grid.setSelectedRows(range(this._grid.getDataLength()));
} else {
this._grid.setSelectedRows(rows);
}
}
getSelectedRows(): number[] {
public getSelectedRows(): number[] {
return this._grid.getSelectedRows();
}
@@ -143,21 +187,6 @@ export class Table<T extends Slick.SlickData> extends Widget implements IThemabl
};
}
onContextMenu(fn: (e: Slick.EventData, data: Slick.OnContextMenuEventArgs<T>) => any): IDisposable;
onContextMenu(fn: (e: DOMEvent, data: Slick.OnContextMenuEventArgs<T>) => any): IDisposable;
onContextMenu(fn: any): IDisposable {
this._grid.onContextMenu.subscribe(fn);
return {
dispose: () => {
this._grid.onContextMenu.unsubscribe(fn);
}
};
}
getCellFromEvent(e: DOMEvent): Slick.Cell {
return this._grid.getCellFromEvent(e);
}
setSelectionModel(model: Slick.SelectionModel<T, Array<Slick.Range>>) {
this._grid.setSelectionModel(model);
}
@@ -194,7 +223,7 @@ export class Table<T extends Slick.SlickData> extends Widget implements IThemabl
this._tableContainer.style.width = sizing.width + 'px';
this._tableContainer.style.height = sizing.height + 'px';
} else {
if (orientation === Orientation.HORIZONTAL) {
if (orientation === Orientation.VERTICAL) {
this._container.style.width = '100%';
this._container.style.height = sizing + 'px';
this._tableContainer.style.width = '100%';
@@ -207,6 +236,7 @@ export class Table<T extends Slick.SlickData> extends Widget implements IThemabl
}
}
this.resizeCanvas();
this.autosizeColumns();
}
autosizeColumns() {

View File

@@ -25,7 +25,7 @@ export class TableBasicView<T> extends View {
super(undefined, viewOpts);
this._container = document.createElement('div');
this._container.className = 'table-view';
this._table = new Table<T>(this._container, data, columns, tableOpts);
this._table = new Table<T>(this._container, { dataProvider: data, columns }, tableOpts);
}
public get table(): Table<T> {
@@ -59,7 +59,7 @@ export class TableHeaderView<T> extends HeaderView {
super(undefined, viewOpts);
this._container = document.createElement('div');
this._container.className = 'table-view';
this._table = new Table<T>(this._container, data, columns, tableOpts);
this._table = new Table<T>(this._container, { dataProvider: data, columns }, tableOpts);
}
public get table(): Table<T> {
@@ -76,7 +76,7 @@ export class TableHeaderView<T> extends HeaderView {
}
protected layoutBody(size: number): void {
this._table.layout(size, Orientation.HORIZONTAL);
this._table.layout(size, Orientation.VERTICAL);
}
focus(): void {
@@ -99,7 +99,7 @@ export class TableCollapsibleView<T> extends AbstractCollapsibleView {
super(undefined, viewOpts);
this._container = document.createElement('div');
this._container.className = 'table-view';
this._table = new Table<T>(this._container, data, columns, tableOpts);
this._table = new Table<T>(this._container, { dataProvider: data, columns }, tableOpts);
}
public render(container: HTMLElement, orientation: Orientation): void {
@@ -143,6 +143,6 @@ export class TableCollapsibleView<T> extends AbstractCollapsibleView {
}
protected layoutBody(size: number): void {
this._table.layout(size, Orientation.HORIZONTAL);
this._table.layout(size, Orientation.VERTICAL);
}
}

View File

@@ -13,4 +13,11 @@ export const disabledInputBackground = registerColor('input.disabled.background'
export const disabledInputForeground = registerColor('input.disabled.foreground', { dark: '#888888', light: '#888888', hc: foreground }, nls.localize('disabledInputBoxForeground', "Disabled Input box foreground."));
export const buttonFocusOutline = registerColor('button.focusOutline', { dark: '#eaeaea', light: '#666666', hc: null }, nls.localize('buttonFocusOutline', "Button outline color when focused."));
export const listFocusAndSelectionBackground = registerColor('list.focusAndSelectionBackground', { dark: '#2c3295', light: '#2c3295', hc: null }, nls.localize('listFocusAndSelectionBackground', "List/Table background color for the selected and focus item when the list/table is active"));
export const listFocusAndSelectionBackground = registerColor('list.focusAndSelectionBackground', { dark: '#2c3295', light: '#2c3295', hc: null }, nls.localize('listFocusAndSelectionBackground', "List/Table background color for the selected and focus item when the list/table is active"));
// SQL Agent Colors
export const tableBackground = registerColor('agent.tableBackground', { light: '#fffffe', dark: '#333333', hc: Color.black }, nls.localize('agentTableBackground', "SQL Agent Table background color."));
export const cellBackground = registerColor('agent.cellBackground', { light: '#faf5f8', dark: Color.black, hc: Color.black }, nls.localize('agentCellBackground', "SQL Agent table cell background color."));
export const tableHoverBackground = registerColor('agent.tableHoverColor', { light: '#dcdcdc', dark: '#444444', hc: null }, nls.localize('agentTableHoverBackground', "SQL Agent table hover background color."));
export const jobsHeadingBackground = registerColor('agent.jobsHeadingColor', { light: '#f4f4f4', dark: '#444444', hc: '#2b56f2' }, nls.localize('agentJobsHeadingColor', "SQL Agent heading background color."));
export const cellBorderColor = registerColor('agent.cellBorderColor', { light: null, dark: null, hc: '#2b56f2' }, nls.localize('agentCellBorderColor', "SQL Agent table cell border color."));

View File

@@ -171,13 +171,17 @@ export class AccountDialog extends Modal {
if (!this.isEmptyLinkedAccount()) {
this.showSplitView();
} else {
this._splitViewContainer.hidden = true;
this._noaccountViewContainer.hidden = false;
this._addAccountButton.focus();
this.showNoAccountContainer();
}
}
private showNoAccountContainer() {
this._splitViewContainer.hidden = true;
this._noaccountViewContainer.hidden = false;
this._addAccountButton.focus();
}
private showSplitView() {
this._splitViewContainer.hidden = false;
this._noaccountViewContainer.hidden = true;
@@ -298,6 +302,10 @@ export class AccountDialog extends Modal {
this.showSplitView();
}
if (this.isEmptyLinkedAccount() && this._noaccountViewContainer.hidden) {
this.showNoAccountContainer();
}
this.layout();
}
}

View File

@@ -92,5 +92,10 @@ configurationRegistry.registerConfiguration({
'description': localize('sql.defaultEngineDescription', 'Default SQL Engine to use. This drives default language provider in .sql files and the default to use when creating a new connection. Valid option is currently MSSQL'),
'default': 'MSSQL'
},
'connection.parseClipboardForConnectionString': {
'type': 'boolean',
'default': true,
'description': localize('connection.parseClipboardForConnectionStringDescription', 'Attempt to parse the contents of the clipboard when the connection dialog is opened or a paste is performed.')
}
}
});
});

View File

@@ -159,7 +159,8 @@ export class GetCurrentConnectionStringAction extends Action {
if (activeInput && (activeInput instanceof QueryInput || activeInput instanceof EditDataInput || activeInput instanceof DashboardInput)
&& this._connectionManagementService.isConnected(activeInput.uri)) {
let includePassword = false;
this._connectionManagementService.getConnectionString(activeInput.uri, includePassword).then(result => {
let connectionProfile = this._connectionManagementService.getConnectionProfile(activeInput.uri);
this._connectionManagementService.getConnectionString(connectionProfile.id, includePassword).then(result => {
let message = result
? result
: nls.localize('connectionAction.connectionString', "Connection string not available");

View File

@@ -263,9 +263,14 @@ export interface IConnectionManagementService {
getActiveConnectionCredentials(profileId: string): { [name: string]: string };
/**
* Get the connection string for the provided connection profile
* Get the connection string for the provided connection ID
*/
getConnectionString(ownerUri: string, includePassword: boolean): Thenable<string>;
getConnectionString(connectionId: string, includePassword: boolean): Thenable<string>;
/**
* Serialize connection string with optional provider
*/
buildConnectionInfo(connectionString: string, provider?: string): Thenable<sqlops.ConnectionInfo>;
}
export const IConnectionDialogService = createDecorator<IConnectionDialogService>('connectionDialogService');

View File

@@ -649,9 +649,9 @@ export class ConnectionManagementService extends Disposable implements IConnecti
}
public getConnectionUriFromId(connectionId: string): string {
let connection = this.getActiveConnections().find(connection => connection.id === connectionId);
if (connection) {
return this.getConnectionUri(connection);
let connectionInfo = this._connectionStatusManager.findConnectionByProfileId(connectionId);
if (connectionInfo) {
return connectionInfo.ownerUri;
} else {
return undefined;
}
@@ -1348,9 +1348,11 @@ export class ConnectionManagementService extends Disposable implements IConnecti
}
/**
* Get the connection string for the provided connection profile
* Get the connection string for the provided connection ID
*/
public getConnectionString(ownerUri: string, includePassword: boolean = false): Thenable<string> {
public getConnectionString(connectionId: string, includePassword: boolean = false): Thenable<string> {
let ownerUri = this.getConnectionUriFromId(connectionId);
if (!ownerUri) {
return Promise.resolve(undefined);
}
@@ -1366,4 +1368,14 @@ export class ConnectionManagementService extends Disposable implements IConnecti
});
});
}
/**
* Serialize connection with options provider
* TODO this could be a map reduce operation
*/
public buildConnectionInfo(connectionString: string, provider: string): Thenable<sqlops.ConnectionInfo> {
return this._providers.get(provider).onReady.then(e => {
return e.buildConnectionInfo(connectionString);
});
}
}

View File

@@ -153,6 +153,19 @@ export class ConnectionProfileGroup implements IConnectionProfileGroup {
return this.parent;
}
public isAncestorOf(node: ConnectionProfileGroup | ConnectionProfile): boolean {
let isAncestor = false;
let currentNode = node;
while (currentNode) {
if (currentNode.parent && currentNode.parent.id === this.id) {
isAncestor = true;
break;
}
currentNode = currentNode.parent;
}
return isAncestor;
}
public static getGroupFullNameParts(groupFullName: string): string[] {
groupFullName = groupFullName ? groupFullName : '';
let groupNames: string[] = groupFullName.split(ConnectionProfileGroup.GroupNameSeparator);

View File

@@ -22,14 +22,18 @@ export class ConnectionStatusManager {
this._providerCapabilitiesMap = {};
}
public findConnection(id: string): ConnectionManagementInfo {
if (id in this._connections) {
return this._connections[id];
public findConnection(uri: string): ConnectionManagementInfo {
if (uri in this._connections) {
return this._connections[uri];
} else {
return undefined;
}
}
public findConnectionByProfileId(profileId: string): ConnectionManagementInfo {
return Object.values(this._connections).find((connection: ConnectionManagementInfo) => connection.connectionProfile.id === profileId);
}
public findConnectionProfile(connectionProfile: IConnectionProfile): ConnectionManagementInfo {
let id = Utils.generateUri(connectionProfile);
return this.findConnection(id);

View File

@@ -34,7 +34,6 @@ import { ITree } from 'vs/base/parts/tree/browser/tree';
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import * as styler from 'vs/platform/theme/common/styler';
import { TPromise } from 'vs/base/common/winjs.base';
import * as DOM from 'vs/base/browser/dom';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
@@ -376,6 +375,7 @@ export class ConnectionDialogWidget extends Modal {
*/
public open(recentConnections: boolean) {
this._panel.showTab(this._recentConnectionTabId);
this.show();
if (recentConnections) {
this._noRecentConnectionBuilder.hide();

View File

@@ -19,6 +19,9 @@ import * as Constants from 'sql/parts/connection/common/constants';
import { ConnectionProfileGroup, IConnectionProfileGroup } from 'sql/parts/connection/common/connectionProfileGroup';
import { attachInputBoxStyler, attachButtonStyler, attachEditableDropdownStyler } from 'sql/common/theme/styler';
import { Dropdown } from 'sql/base/browser/ui/editableDropdown/dropdown';
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService';
import { ConnectionProfile } from '../common/connectionProfile';
import * as sqlops from 'sqlops';
@@ -32,6 +35,8 @@ import { OS, OperatingSystem } from 'vs/base/common/platform';
import { Builder, $ } from 'vs/base/browser/builder';
import { MessageType } from 'vs/base/browser/ui/inputbox/inputBox';
import { endsWith, startsWith } from 'vs/base/common/strings';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
export class ConnectionWidget {
private _builder: Builder;
@@ -85,7 +90,12 @@ export class ConnectionWidget {
callbacks: IConnectionComponentCallbacks,
providerName: string,
@IThemeService private _themeService: IThemeService,
@IContextViewService private _contextViewService: IContextViewService) {
@IContextViewService private _contextViewService: IContextViewService,
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService,
@ICapabilitiesService private _capabilitiesService: ICapabilitiesService,
@IClipboardService private _clipboardService: IClipboardService,
@IConfigurationService private _configurationService: IConfigurationService
) {
this._callbacks = callbacks;
this._toDispose = [];
this._optionsMaps = {};
@@ -120,9 +130,29 @@ export class ConnectionWidget {
if (this._authTypeSelectBox) {
this.onAuthTypeSelected(this._authTypeSelectBox.value);
}
DOM.addDisposableListener(container, 'paste', e => {
this._handleClipboard();
});
DOM.append(container, this._builder.getHTMLElement());
}
private _handleClipboard(): void {
if (this._configurationService.getValue<boolean>('connection.parseClipboardForConnectionString')) {
let paste = this._clipboardService.readText();
this._connectionManagementService.buildConnectionInfo(paste, this._providerName).then(e => {
if (e) {
let profile = new ConnectionProfile(this._capabilitiesService, this._providerName);
profile.options = e.options;
if (profile.serverName) {
this.initDialog(profile);
}
}
});
}
}
private fillInConnectionForm(): void {
let serverNameOption = this._optionsMaps[ConnectionOptionSpecialType.serverName];
let serverNameBuilder = DialogHelper.appendRow(this._tableContainer, serverNameOption.displayName, 'connection-label', 'connection-input');
@@ -130,7 +160,7 @@ export class ConnectionWidget {
validationOptions: {
validation: (value: string) => {
if (!value) {
return ({ type: MessageType.ERROR, content: localize('connectionWidget.missingRequireField', '{0} is required.', serverNameOption.displayName)});
return ({ type: MessageType.ERROR, content: localize('connectionWidget.missingRequireField', '{0} is required.', serverNameOption.displayName) });
} else if (startsWith(value, ' ') || endsWith(value, ' ')) {
return ({ type: MessageType.WARNING, content: localize('connectionWidget.fieldWillBeTrimmed', '{0} will be trimmed.', serverNameOption.displayName) });
}
@@ -173,7 +203,7 @@ export class ConnectionWidget {
placeholder: this._defaultDatabaseName,
maxHeight: 125,
ariaLabel: databaseOption.displayName,
actionLabel: localize('toggleDatabaseNameDropdown', 'Select Database Toggle Dropdown')
actionLabel: localize('connectionWidget.toggleDatabaseNameDropdown', 'Select Database Toggle Dropdown')
});
let serverGroupLabel = localize('serverGroup', 'Server group');
@@ -354,6 +384,7 @@ export class ConnectionWidget {
}
public focusOnOpen(): void {
this._handleClipboard();
this._serverNameInputBox.focus();
this.focusPasswordIfNeeded();
this.clearValidationMessages();

View File

@@ -69,7 +69,7 @@ ExtensionsRegistry.registerExtensionPoint<IDashboardContainerContrib | IDashboar
return;
}
if (Object.keys(container).length !== 1) {
extension.collector.error(localize('dashboardTab.contribution.moreThanOneDashboardContainersError', 'Exactly 1 dashboard container must be defined per space.'));
extension.collector.error(localize('dashboardContainer.contribution.moreThanOneDashboardContainersError', 'Exactly 1 dashboard container must be defined per space.'));
return;
}

View File

@@ -33,6 +33,7 @@ import { IStorageService } from 'vs/platform/storage/common/storage';
import { TPromise } from 'vs/base/common/winjs.base';
import { toDisposable } from 'vs/base/common/lifecycle';
import { isPromiseCanceledError } from 'vs/base/common/errors';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
const insightRegistry = Registry.as<IInsightRegistry>(Extensions.InsightContribution);
@@ -72,7 +73,7 @@ export class InsightsWidget extends DashboardWidget implements IDashboardWidget,
@Inject(IInstantiationService) private instantiationService: IInstantiationService,
@Inject(IStorageService) private storageService: IStorageService,
@Inject(IWorkspaceContextService) private workspaceContextService: IWorkspaceContextService,
@Inject(IConfigurationService) private readonly _configurationService: IConfigurationService
) {
super();
this.insightConfig = <IInsightsConfig>this._config.widget['insights-widget'];
@@ -231,7 +232,7 @@ export class InsightsWidget extends DashboardWidget implements IDashboardWidget,
if (componentInstance.setConfig) {
componentInstance.setConfig(this.insightConfig.type[this._typeKey]);
}
componentInstance.data = { columns: result.columnInfo.map(item => item.columnName), rows: result.rows.map(row => row.map(item => (item.invariantCultureDisplayValue === null || item.invariantCultureDisplayValue === undefined) ? item.displayValue : item.invariantCultureDisplayValue)) };
componentInstance.data = { columns: result.columnInfo.map(item => item.columnName), rows: result.rows.map(row => row.map(item => (item.invariantCultureDisplayValue === null || item.invariantCultureDisplayValue === undefined) ? item.displayValue : item.invariantCultureDisplayValue)) };
if (componentInstance.init) {
componentInstance.init();
@@ -275,6 +276,14 @@ export class InsightsWidget extends DashboardWidget implements IDashboardWidget,
this._typeKey = Object.keys(this.insightConfig.type)[0];
// When the editor.accessibilitySupport setting is on, we will force the chart type to be table.
// so that the information is accessible to the user.
// count chart type is already a text based chart, we don't have to apply this rule for it.
let isAccessibilitySupportOn = this._configurationService.getValue('editor.accessibilitySupport') === 'on';
if (isAccessibilitySupportOn && this._typeKey !== 'count') {
this._typeKey = 'table';
}
if (types.isStringArray(this.insightConfig.query)) {
this.insightConfig.query = this.insightConfig.query.join(' ');
} else if (this.insightConfig.queryFile) {

View File

@@ -19,6 +19,8 @@ import { IColorTheme, IWorkbenchThemeService } from 'vs/workbench/services/theme
import * as nls from 'vs/nls';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
declare var Chart: any;
export enum ChartType {
Bar = 'bar',
Doughnut = 'doughnut',
@@ -81,8 +83,8 @@ export interface IChartConfig {
}
export const defaultChartConfig: IChartConfig = {
labelFirstColumn: false,
columnsAsLabels: false,
labelFirstColumn: true,
columnsAsLabels: true,
legendPosition: LegendPosition.Top,
dataDirection: DataDirection.Vertical
};
@@ -161,11 +163,17 @@ export abstract class ChartInsight extends Disposable implements IInsightsView {
protected updateTheme(e: IColorTheme): void {
let foregroundColor = e.getColor(colors.editorForeground);
let foreground = foregroundColor ? foregroundColor.toString() : null;
let backgroundColor = e.getColor(colors.editorBackground);
let background = backgroundColor ? backgroundColor.toString() : null;
let options = {
legend: {
labels: {
fontColor: foreground
}
},
viewArea: {
backgroundColor: background
}
};
this.options = mixin({}, mixin(this.options, options));
@@ -348,3 +356,13 @@ function isValidData(data: IInsightData): boolean {
return true;
}
Chart.pluginService.register({
beforeDraw: function (chart) {
if (chart.config.options.viewArea && chart.config.options.viewArea.backgroundColor) {
var ctx = chart.chart.ctx;
ctx.fillStyle = chart.config.options.viewArea.backgroundColor;
ctx.fillRect(0, 0, chart.chart.width, chart.chart.height);
}
}
});

View File

@@ -24,7 +24,7 @@ const properties: IJSONSchema = {
},
yAxisLabel: {
type: 'string',
description: nls.localize('yAxisLabel', "Label for the y axis")
description: nls.localize('barchart.yAxisLabel', "Label for the y axis")
},
xAxisMin: {
type: 'number',
@@ -36,7 +36,7 @@ const properties: IJSONSchema = {
},
xAxisLabel: {
type: 'string',
description: nls.localize('xAxisLabel', "Label for the x axis")
description: nls.localize('barchart.xAxisLabel', "Label for the x axis")
}
}
};

View File

@@ -62,7 +62,7 @@ export default class TableInsight extends Disposable implements IInsightsView, O
private createTable() {
if (!this.table) {
this.table = new Table(this._elementRef.nativeElement, this.dataView, this.columns, { showRowNumber: true });
this.table = new Table(this._elementRef.nativeElement, { dataProvider: this.dataView, columns: this.columns }, { showRowNumber: true });
this.table.setSelectionModel(new CellSelectionModel());
this._register(attachTableStyler(this.table, this.themeService));
}

View File

@@ -86,7 +86,7 @@ export class TasksWidget extends DashboardWidget implements IDashboardWidget, On
}).filter(i => !!i);
}
this._tasks = tasks.map(i => MenuRegistry.getCommand(i)).filter(v => !!v);
this._tasks = tasks.map(i => TaskRegistry.getCommandActionById(i)).filter(v => !!v);
}
ngOnInit() {

View File

@@ -236,7 +236,7 @@ export class RestoreDialog extends Modal {
{
strictSelection: false,
ariaLabel: LocalizedStrings.TARGETDATABASE,
actionLabel: localize('toggleDatabaseNameDropdown', 'Select Database Toggle Dropdown')
actionLabel: localize('restoreDialog.toggleDatabaseNameDropdown', 'Select Database Toggle Dropdown')
}
);
this._databaseDropdown.onValueChange(s => {
@@ -272,7 +272,8 @@ export class RestoreDialog extends Modal {
this._restorePlanTableContainer = labelContainer.getHTMLElement();
labelContainer.hide();
this._restorePlanData = new TableDataView<Slick.SlickData>();
this._restorePlanTable = new Table<Slick.SlickData>(labelContainer.getHTMLElement(), this._restorePlanData, this._restorePlanColumn, { enableColumnReorder: false });
this._restorePlanTable = new Table<Slick.SlickData>(labelContainer.getHTMLElement(),
{ dataProvider: this._restorePlanData, columns: this._restorePlanColumn }, { enableColumnReorder: false });
this._restorePlanTable.setSelectionModel(new RowSelectionModel({ selectActiveRow: false }));
this._restorePlanTable.onSelectedRowsChanged((e, data) => this.backupFileCheckboxChanged(e, data));
});
@@ -328,7 +329,8 @@ export class RestoreDialog extends Modal {
field: 'restoreAs'
}];
this._fileListData = new TableDataView<FileListElement>();
this._fileListTable = new Table<FileListElement>(fileNameContainer.getHTMLElement(), this._fileListData, columns, { enableColumnReorder: false });
this._fileListTable = new Table<FileListElement>(fileNameContainer.getHTMLElement(),
{ dataProvider : this._fileListData, columns } , { enableColumnReorder: false });
this._fileListTable.setSelectionModel(new RowSelectionModel());
});
});

View File

@@ -4,14 +4,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ISlickColumn, IObservableCollection, IGridDataRow } from 'angular2-slickgrid';
export interface ISlickRange {
fromCell: number;
fromRow: number;
toCell: number;
toRow: number;
}
import { ISlickColumn, VirtualizedCollection } from 'angular2-slickgrid';
export interface IGridIcon {
showCondition: () => boolean;
@@ -41,7 +34,7 @@ export interface IGridIcon {
}
export interface IGridDataSet {
dataRows: IObservableCollection<IGridDataRow>;
dataRows: VirtualizedCollection<{}>;
columnDefinitions: ISlickColumn<any>[];
resized: any; // EventEmitter<any>;
totalRows: number;
@@ -61,7 +54,7 @@ export enum SaveFormat {
export interface IGridInfo {
batchIndex: number;
resultSetNumber: number;
selection: ISlickRange[];
selection: Slick.Range[];
gridIndex: number;
rowIndex?: number;
}
@@ -69,5 +62,5 @@ export interface ISaveRequest {
format: SaveFormat;
batchIndex: number;
resultSetNumber: number;
selection: ISlickRange[];
selection: Slick.Range[];
}

View File

@@ -9,170 +9,168 @@
*/
.errorMessage {
color: var(--color-error);
color: var(--color-error);
}
.batchMessage {
padding-left: 20px;
padding-left: 20px;
}
.slick-cell a, a:link {
color: var(--color-grid-link);
text-decoration: underline;
color: var(--color-grid-link);
text-decoration: underline;
}
.slick-cell a:hover {
color: var(--color-grid-link-hover);
color: var(--color-grid-link-hover);
}
.resultsMessageValue a, a:link {
color: var(--color-grid-link);
text-decoration: underline;
color: var(--color-grid-link);
text-decoration: underline;
}
.resultsMessageValue a:hover {
color: var(--color-grid-link-hover);
color: var(--color-grid-link-hover);
}
.grid .slick-cell.dirtyCell {
color: var(--color-grid-dirty-text);
background-color: var(--color-grid-dirty-background);
color: var(--color-grid-dirty-text);
background-color: var(--color-grid-dirty-background);
}
.grid .slick-cell.dirtyRowHeader {
background-color: var(--color-grid-dirty-background);
background-color: var(--color-grid-dirty-background);
}
.slick-cell.dirtyRowHeader > .row-number {
color: var(--color-grid-dirty-text);
font-weight: 500;
color: var(--color-grid-dirty-text);
font-weight: 500;
}
/*
* vs theme
*
*/
.vs .slickgridContainer {
--color-content: #101010;
--color-content-disabled: #a9a9a9;
--color-error: #E81123;
--color-success: #7CD300;
--color-bg-header: hsla(0,0%,50%,.2);
--color-resize-handle: grey;
--color-bg-content-header: #F5F5F5; /* used for color of grid headers */
--color-cell-border-active: grey;
--color-cell-bg-grid-selected: rgb(173, 214, 255);
--color-grid-link: #0078D7;
--color-grid-link-hover: #0b93ff;
--color-grid-dirty-background: #CCC;
--color-grid-dirty-text: #101010;
--color-content: #101010;
--color-content-disabled: #a9a9a9;
--color-error: #E81123;
--color-success: #7CD300;
--color-bg-header: hsla(0,0%,50%,.2);
--color-resize-handle: grey;
--color-bg-content-header: #F5F5F5; /* used for color of grid headers */
--color-cell-border-active: grey;
--color-cell-bg-grid-selected: rgb(173, 214, 255);
--color-grid-link: #0078D7;
--color-grid-link-hover: #0b93ff;
--color-grid-dirty-background: #CCC;
--color-grid-dirty-text: #101010;
}
/* grid styling */
.vs slick-grid.active .grid .slick-cell.active {
border: dotted 1px var(--color-cell-border-active);
border-color: var(--color-cell-border-active);
}
.vs slick-grid.active .grid .slick-cell.selected {
background-color: var(--color-cell-bg-grid-selected);
background-color: var(--color-cell-bg-grid-selected);
}
.vs .grid .slick-cell.selected .grid-cell-value-container.missing-value {
color: var(--color-content) !important;
color: var(--color-content) !important;
}
.vs .boxRow.content.horzBox.slickgrid {
border: solid 1px #EEEEF2;
border: solid 1px #EEEEF2;
}
/* icons */
.vs .gridIcon.extendFullScreen {
/* ExtendToFullScreen_16x_vscode */
background-image: url("extendFullScreen.svg");
.vs .icon.extendFullScreen {
/* ExtendToFullScreen_16x_vscode */
background-image: url("extendFullScreen.svg");
}
.vs .gridIcon.exitFullScreen {
/* ExitFullScreen_16x_vscode */
background-image: url("exitFullScreen.svg");
.vs .icon.exitFullScreen {
/* ExitFullScreen_16x_vscode */
background-image: url("exitFullScreen.svg");
}
.vs .gridIcon.saveJson {
/* ResultToJSON_16x_vscode */
background-image: url("saveJson.svg");
.vs .icon.saveJson {
/* ResultToJSON_16x_vscode */
background-image: url("saveJson.svg");
}
.vs .gridIcon.saveCsv {
/* ResultToCSV_16x_vscode */
background-image: url("saveCsv.svg");
.vs .icon.saveCsv {
/* ResultToCSV_16x_vscode */
background-image: url("saveCsv.svg");
}
.vs .gridIcon.saveExcel {
/* ResultToXlsx_16x_vscode */
background-image: url("saveExcel.svg");
.vs .icon.saveExcel {
/* ResultToXlsx_16x_vscode */
background-image: url("saveExcel.svg");
}
.vs .gridIcon.viewChart {
/* ResultToXlsx_16x_vscode */
background-image: url("viewChart.svg");
.vs .icon.viewChart {
/* ResultToXlsx_16x_vscode */
background-image: url("viewChart.svg");
}
/* headers */
.vs .resultsMessageHeader {
background: var(--color-bg-header);
color: var(--color-content);
background: var(--color-bg-header);
color: var(--color-content);
}
.vs .resultsViewCollapsible:not(.collapsed) {
background-image: url("uncollapsedArrow.svg");
background-repeat: no-repeat;
background-position: 2px;
background-image: url("uncollapsedArrow.svg");
background-repeat: no-repeat;
background-position: 2px;
}
.vs .resultsViewCollapsible {
background-image: url("collapsedArrow.svg");
background-repeat: no-repeat;
background-position: 2px;
background-image: url("collapsedArrow.svg");
background-repeat: no-repeat;
background-position: 2px;
}
.vs .queryResultsShortCut {
color: grey;
color: grey;
}
/* scroll bar */
.vs ::-webkit-scrollbar {
width: 14px;
height: 10px;
width: 14px;
height: 10px;
}
.vs ::-webkit-scrollbar-thumb {
background: hsla(0,0%,47%,.4);
background: hsla(0,0%,47%,.4);
}
.vs ::-webkit-scrollbar-thumb:hover {
background: hsla(0,0%,39%,.7);
background: hsla(0,0%,39%,.7);
}
.vs ::-webkit-scrollbar-thumb:active {
background: rgba(85,85,85,0.8);
background: rgba(85,85,85,0.8);
}
.vs ::-webkit-scrollbar-track {
background: var(--background-color);
background: var(--background-color);
}
.vs ::-webkit-scrollbar-corner {
background: transparent;
background: transparent;
}
.vs .monaco-workbench input {
color: var(--color-content);
color: var(--color-content);
}
.vs .monaco-workbench .input {
background-color: white;
background-color: white;
}
/*
@@ -181,131 +179,139 @@
*/
.vs-dark .slickgridContainer {
--color-content: #E5E5E5;
--color-content-disabled: grey;
--color-error: #E81123;
--color-success: #7CD300;
--color-bg-header: hsla(0,0%,50%,.2); /* used for pane toolbars */
--color-resize-handle: #4d4d4d;
--color-bg-content-header: #333334; /* used for color of grid headers */
--color-cell-border-active: white;
--color-cell-bg-grid-selected: rgb(38, 79, 120);
--color-grid-link: #FF6000;
--color-grid-link-hover: #ff8033;
--color-grid-dirty-background: #4d4d4d;
--color-grid-dirty-text: #E5E5E5;
--color-content: #E5E5E5;
--color-content-disabled: grey;
--color-error: #E81123;
--color-success: #7CD300;
--color-bg-header: hsla(0,0%,50%,.2); /* used for pane toolbars */
--color-resize-handle: #4d4d4d;
--color-bg-content-header: #333334; /* used for color of grid headers */
--color-cell-border-active: white;
--color-cell-bg-grid-selected: rgb(38, 79, 120);
--color-grid-link: #FF6000;
--color-grid-link-hover: #ff8033;
--color-grid-dirty-background: #4d4d4d;
--color-grid-dirty-text: #E5E5E5;
}
/* grid styling */
.vs-dark slick-grid.active .grid .slick-cell.active {
border: dotted 1px var(--color-cell-border-active);
border-color: var(--color-cell-border-active);
}
.vs-dark slick-grid.active .grid .slick-cell.selected {
background-color: var(--color-cell-bg-grid-selected);
background-color: var(--color-cell-bg-grid-selected);
}
.vs-dark .grid .slick-cell.selected .grid-cell-value-container.missing-value {
color: var(--color-content) !important;
color: var(--color-content) !important;
}
.vs-dark .boxRow.content.horzBox.slickgrid {
border: solid 1px #2D2D30;
border: solid 1px #2D2D30;
}
/* icons */
.vs-dark .gridIcon.extendFullScreen,
.hc-black .gridIcon.extendFullScreen {
/* ExtendToFullScreen_16x_vscode_inverse.svg */
background-image: url("extendFullScreen_inverse.svg");
.vs-dark .icon.extendFullScreen,
.hc-black .icon.extendFullScreen {
/* ExtendToFullScreen_16x_vscode_inverse.svg */
background-image: url("extendFullScreen_inverse.svg");
}
.vs-dark .gridIcon.exitFullScreen,
.hc-black .gridIcon.exitFullScreen {
/* ExitFullScreen_16x_vscode_inverse.svg */
background-image: url("exitFullScreen_inverse.svg");
.vs-dark .icon.exitFullScreen,
.hc-black .icon.exitFullScreen {
/* ExitFullScreen_16x_vscode_inverse.svg */
background-image: url("exitFullScreen_inverse.svg");
}
.vs-dark .gridIcon.saveJson,
.hc-black .gridIcon.saveJson {
/* ResultToJSON_16x_vscode_inverse.svg */
background-image: url("saveJson_inverse.svg");
.vs-dark .icon.saveJson,
.hc-black .icon.saveJson {
/* ResultToJSON_16x_vscode_inverse.svg */
background-image: url("saveJson_inverse.svg");
}
.vs-dark .gridIcon.saveCsv,
.hc-black .gridIcon.saveCsv {
/* ResultToCSV_16x_vscode_inverse.svg */
background-image: url("saveCsv_inverse.svg");
.vs-dark .icon.saveCsv,
.hc-black .icon.saveCsv {
/* ResultToCSV_16x_vscode_inverse.svg */
background-image: url("saveCsv_inverse.svg");
}
.vs-dark .gridIcon.saveExcel,
.hc-black .gridIcon.saveExcel {
/* ResultToXlsx_16x_vscode_inverse.svg */
background-image: url("saveExcel_inverse.svg");
.vs-dark .icon.saveExcel,
.hc-black .icon.saveExcel {
/* ResultToXlsx_16x_vscode_inverse.svg */
background-image: url("saveExcel_inverse.svg");
}
.vs-dark .gridIcon.viewChart,
.hc-black .gridIcon.viewChart {
/* ResultToXlsx_16x_vscode */
background-image: url("viewChart_inverse.svg");
.vs-dark .icon.viewChart,
.hc-black .icon.viewChart {
/* ResultToXlsx_16x_vscode */
background-image: url("viewChart_inverse.svg");
}
.grid-panel .action-label.icon {
height: 35px;
line-height: 35px;
min-width: 28px;
background-size: 16px;
background-position: center center;
background-repeat: no-repeat;
}
/* headers */
.vs-dark .resultsMessageHeader {
background: var(--color-bg-header);
color: var(--color-content);
background: var(--color-bg-header);
color: var(--color-content);
}
.vs-dark .resultsViewCollapsible:not(.collapsed),
.hc-black .resultsViewCollapsible:not(.collapsed) {
background-image:url("uncollapsedArrow_inverse.svg");
background-repeat:no-repeat;
background-position: 2px;
background-image:url("uncollapsedArrow_inverse.svg");
background-repeat:no-repeat;
background-position: 2px;
}
.vs-dark .resultsViewCollapsible,
.hc-black .resultsViewCollapsible {
background-image: url("collapsedArrow_inverse.svg");
background-repeat:no-repeat;
background-position: 2px;
background-image: url("collapsedArrow_inverse.svg");
background-repeat:no-repeat;
background-position: 2px;
}
.vs-dark .queryResultsShortCut {
color: grey;
color: grey;
}
/* scroll bar */
.vs-dark ::-webkit-scrollbar {
width: 14px;
height: 10px;
width: 14px;
height: 10px;
}
.vs-dark ::-webkit-scrollbar-thumb {
background: hsla(0,0%,47%,.4);
background: hsla(0,0%,47%,.4);
}
.vs-dark ::-webkit-scrollbar-thumb:hover {
background: hsla(0,0%,39%,.7);
background: hsla(0,0%,39%,.7);
}
.vs-dark ::-webkit-scrollbar-thumb:active {
background: rgba(85,85,85,0.8);
background: rgba(85,85,85,0.8);
}
.vs-dark ::-webkit-scrollbar-track {
background: var(--background-color);
background: var(--background-color);
}
.vs-dark ::-webkit-scrollbar-corner {
background: transparent;
background: transparent;
}
.vs-dark .monaco-workbench input, .vs-dark .monaco-workbench .input {
color: var(--color-content);
background-color: #3C3C3C;
color: var(--color-content);
background-color: #3C3C3C;
}
/*
@@ -313,55 +319,55 @@
*
*/
.hc-black .slickgridContainer {
--color-content: #E5E5E5;
--color-content-disabled: grey;
--color-error: #E81123;
--color-success: #7CD300;
--color-bg-header: hsla(0,0%,50%,.2); /* used for pane toolbars */
--color-resize-handle: #4d4d4d;
--color-bg-content-header: #333334; /* used for color of grid headers */
--color-cell-border-active: orange;
--color-cell-bg-grid-selected: rgb(38, 79, 120);
--color-grid-link: #FF6000;
--color-grid-link-hover: #ff8033;
--color-grid-dirty-background: #FFF;
--color-grid-dirty-text: #000;
.hc-black .slickgridContainer {
--color-content: #E5E5E5;
--color-content-disabled: grey;
--color-error: #E81123;
--color-success: #7CD300;
--color-bg-header: hsla(0,0%,50%,.2); /* used for pane toolbars */
--color-resize-handle: #4d4d4d;
--color-bg-content-header: #333334; /* used for color of grid headers */
--color-cell-border-active: orange;
--color-cell-bg-grid-selected: rgb(38, 79, 120);
--color-grid-link: #FF6000;
--color-grid-link-hover: #ff8033;
--color-grid-dirty-background: #FFF;
--color-grid-dirty-text: #000;
}
/* grid styling */
.hc-black slick-grid.active .grid .slick-cell.active {
border: solid 1px var(--color-cell-border-active);
border-color: var(--color-cell-border-active);
}
.hc-black slick-grid.active .grid .slick-cell.selected {
background-color: var(--color-cell-bg-grid-selected);
background-color: var(--color-cell-bg-grid-selected);
}
.hc-black .grid .slick-cell.selected .grid-cell-value-container.missing-value {
color: var(--color-content) !important;
color: var(--color-content) !important;
}
.hc-black .boxRow.content.horzBox.slickgrid {
border: solid 1px #2D2D30;
border: solid 1px #2D2D30;
}
/* headers */
.hc-black .resultsMessageHeader {
background: var(--color-bg-header);
color: var(--color-content);
background: var(--color-bg-header);
color: var(--color-content);
}
.hc-black .queryResultsShortCut {
color: grey;
color: grey;
}
/* scroll bar */
.hc-black ::-webkit-scrollbar {
width: 14px;
height: 10px;
width: 14px;
height: 10px;
}
.hc-black ::-webkit-scrollbar-thumb {
@@ -377,14 +383,14 @@
}
.hc-black ::-webkit-scrollbar-track {
background: var(--background-color);
background: var(--background-color);
}
.hc-black ::-webkit-scrollbar-corner {
background: transparent;
background: transparent;
}
.hc-black .monaco-workbench input {
color: #000;
background-color: #FFF;
color: #000;
background-color: #FFF;
}

View File

@@ -14,7 +14,6 @@ import { IQueryModelService } from 'sql/parts/query/execution/queryModel';
import { ResultSerializer } from 'sql/parts/query/common/resultSerializer';
import { ISaveRequest } from 'sql/parts/grid/common/interfaces';
import { ISlickRange } from 'angular2-slickgrid';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService';
@@ -184,7 +183,7 @@ export class DataService {
* @param resultId The result id of the result to copy from
* @param includeHeaders [Optional]: Should column headers be included in the copy selection
*/
copyResults(selection: ISlickRange[], batchId: number, resultId: number, includeHeaders?: boolean): void {
copyResults(selection: Slick.Range[], batchId: number, resultId: number, includeHeaders?: boolean): void {
this._queryModel.copyResults(this._uri, selection, batchId, resultId, includeHeaders);
}

View File

@@ -13,7 +13,7 @@ import 'vs/css!sql/parts/grid/media/slickGrid';
import 'vs/css!./media/editData';
import { ElementRef, ChangeDetectorRef, OnInit, OnDestroy, Component, Inject, forwardRef, EventEmitter } from '@angular/core';
import { IGridDataRow, VirtualizedCollection, ISlickRange } from 'angular2-slickgrid';
import { VirtualizedCollection } from 'angular2-slickgrid';
import { IGridDataSet } from 'sql/parts/grid/common/interfaces';
import * as Services from 'sql/parts/grid/services/sharedServices';
@@ -76,10 +76,10 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
public onIsCellEditValid: (row: number, column: number, newValue: any) => boolean;
public onIsColumnEditable: (column: number) => boolean;
public overrideCellFn: (rowNumber, columnId, value?, data?) => string;
public loadDataFunction: (offset: number, count: number) => Promise<IGridDataRow[]>;
public loadDataFunction: (offset: number, count: number) => Promise<{}[]>;
private savedViewState: {
gridSelections: ISlickRange[];
gridSelections: Slick.Range[];
scrollTop;
scrollLeft;
};
@@ -153,6 +153,7 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
self.dataSet = undefined;
self.placeHolderDataSets = [];
self.renderedDataSets = self.placeHolderDataSets;
this._cd.detectChanges();
self.totalElapsedTimeSpan = undefined;
self.complete = false;
@@ -180,23 +181,31 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
};
// Setup a function for generating a promise to lookup result subsets
this.loadDataFunction = (offset: number, count: number): Promise<IGridDataRow[]> => {
return new Promise<IGridDataRow[]>((resolve, reject) => {
this.loadDataFunction = (offset: number, count: number): Promise<{}[]> => {
return new Promise<{}[]>((resolve, reject) => {
self.dataService.getEditRows(offset, count).subscribe(result => {
let rowIndex = offset;
let gridData: IGridDataRow[] = result.subset.map(row => {
self.idMapping[rowIndex] = row.id;
rowIndex++;
return {
values: [{}].concat(row.cells.map(c => {
return mixin({ ariaLabel: escape(c.displayValue) }, c);
})), row: row.id
};
let gridData = result.subset.map(r => {
let dataWithSchema = {};
// skip the first column since its a number column
for (let i = 1; i < this.dataSet.columnDefinitions.length; i++) {
dataWithSchema[this.dataSet.columnDefinitions[i].field] = r.cells[i - 1].displayValue;
}
return dataWithSchema;
});
// let rowIndex = offset;
// let gridData: IGridDataRow[] = result.subset.map(row => {
// self.idMapping[rowIndex] = row.id;
// rowIndex++;
// return {
// values: [{}].concat(row.cells.map(c => {
// return mixin({ ariaLabel: escape(c.displayValue) }, c);
// })), row: row.id
// };
// });
// Append a NULL row to the end of gridData
let newLastRow = gridData.length === 0 ? 0 : (gridData[gridData.length - 1].row + 1);
gridData.push({ values: self.dataSet.columnDefinitions.map(cell => { return { displayValue: 'NULL', isNull: false }; }), row: newLastRow });
// let newLastRow = gridData.length === 0 ? 0 : (gridData[gridData.length - 1].row + 1);
// gridData.push({ values: self.dataSet.columnDefinitions.map(cell => { return { displayValue: 'NULL', isNull: false }; }), row: newLastRow });
resolve(gridData);
});
});
@@ -304,8 +313,8 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
self.currentCell = {
row: row,
column: column,
isEditable: self.dataSet.columnDefinitions[column - 1]
? self.dataSet.columnDefinitions[column - 1].isEditable
isEditable: self.dataSet.columnDefinitions[column]
? self.dataSet.columnDefinitions[column].isEditable
: false
};
});
@@ -356,7 +365,7 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
self.windowSize,
resultSet.rowCount,
this.loadDataFunction,
index => { return { values: [] }; }
index => { return {}; }
),
columnDefinitions: [rowNumberColumn.getColumnDefinition()].concat(resultSet.columnInfo.map((c, i) => {
let isLinked = c.isXml || c.isJson;
@@ -581,14 +590,17 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
}
private saveViewState(): void {
let gridSelections = this.slickgrids.toArray()[0].getSelectedRanges();
let viewport = ((this.slickgrids.toArray()[0] as any)._grid.getCanvasNode() as HTMLElement).parentElement;
let grid = this.slickgrids.toArray()[0]
if (grid) {
let gridSelections = grid.getSelectedRanges();
let viewport = ((grid as any)._grid.getCanvasNode() as HTMLElement).parentElement;
this.savedViewState = {
gridSelections,
scrollTop: viewport.scrollTop,
scrollLeft: viewport.scrollLeft
};
this.savedViewState = {
gridSelections,
scrollTop: viewport.scrollTop,
scrollLeft: viewport.scrollLeft
};
}
}
private restoreViewState(): void {

View File

@@ -13,7 +13,7 @@ import 'vs/css!sql/parts/grid/media/slickGrid';
import { Subscription, Subject } from 'rxjs/Rx';
import { ElementRef, QueryList, ChangeDetectorRef, ViewChildren } from '@angular/core';
import { IGridDataRow, ISlickRange, SlickGrid, FieldType } from 'angular2-slickgrid';
import { SlickGrid } from 'angular2-slickgrid';
import { toDisposableSubscription } from 'sql/parts/common/rxjsUtils';
import * as Constants from 'sql/parts/query/common/constants';
import * as LocalizedConstants from 'sql/parts/query/common/localizedConstants';
@@ -226,11 +226,11 @@ export abstract class GridParentComponent {
this.messagesFocussedContextKey.set(false);
}
protected getSelection(index?: number): ISlickRange[] {
protected getSelection(index?: number): Slick.Range[] {
let selection = this.slickgrids.toArray()[index || this.activeGrid].getSelectedRanges();
if (selection) {
selection = selection.map(c => { return <ISlickRange>{ fromCell: c.fromCell - 1, toCell: c.toCell - 1, toRow: c.toRow, fromRow: c.fromRow }; });
return selection;
selection = selection.map(c => { return <Slick.Range>{ fromCell: c.fromCell - 1, toCell: c.toCell - 1, toRow: c.toRow, fromRow: c.fromRow }; });
return selection;
} else {
return undefined;
}
@@ -332,7 +332,7 @@ export abstract class GridParentComponent {
/**
* Send save result set request to service
*/
handleContextClick(event: { type: string, batchId: number, resultId: number, index: number, selection: ISlickRange[] }): void {
handleContextClick(event: { type: string, batchId: number, resultId: number, index: number, selection: Slick.Range[] }): void {
switch (event.type) {
case 'savecsv':
this.dataService.sendSaveRequest({ batchIndex: event.batchId, resultSetNumber: event.resultId, format: SaveFormat.CSV, selection: event.selection });

View File

@@ -7,9 +7,9 @@
</div>
<div class="angular-modal-body-content chart-viewer" style="flex:1 1 auto; border-left: 1px solid; margin: 5px;">
<div style="position: relative; width: 100%">
<div class="dialog-label" id="chartTypeLabel">{{localizedStrings.CHART_TYPE}}</div>
<div class="dialog-label">{{localizedStrings.CHART_TYPE}}</div>
<select-box #chartTypeSelect class="input-divider"
aria-labelledby="chartTypeLabel"
[aria-label]="localizedStrings.CHART_TYPE"
[options]="insightRegistry.getAllIds()"
[selectedOption]="getDefaultChartType()"
[onlyEmitOnChange]="true"
@@ -150,7 +150,8 @@
-->
<ng-template #legendInput>
<div class="dialog-label" id="legendLabel">{{localizedStrings.LEGEND}}</div>
<select-box class="input-divider" aria-labelledby="legendLabel"
<select-box class="input-divider"
[aria-label]="localizedStrings.LEGEND"
[options]="legendOptions"
[selectedOption]="_chartConfig.legendPosition"
[onlyEmitOnChange]="true"
@@ -214,18 +215,23 @@
<ng-template #yAxisLabelInput>
<span>
{{localizedStrings.Y_AXIS_LABEL}}
<input-box (onDidChange)="setConfigValue('yAxisLabel', $event)"></input-box>
<input-box (onDidChange)="setConfigValue('yAxisLabel', $event)"
[aria-label]="localizedStrings.Y_AXIS_LABEL"></input-box>
</span>
</ng-template>
<ng-template #yAxisMinMaxInput>
<span>
{{localizedStrings.Y_AXIS_MAX_VAL}}
<input-box [type]="'number'" (onDidChange)="setConfigValue('yAxisMax', $event)"></input-box>
<input-box [type]="'number'"
(onDidChange)="setConfigValue('yAxisMax', $event)"
[aria-label]="localizedStrings.Y_AXIS_MAX_VAL"></input-box>
</span>
<span>
{{localizedStrings.Y_AXIS_MIN_VAL}}
<input-box [type]="'number'" (onDidChange)="setConfigValue('yAxisMin', $event)"></input-box>
<input-box [type]="'number'"
(onDidChange)="setConfigValue('yAxisMin', $event)"
[aria-label]="localizedStrings.Y_AXIS_MIN_VAL"></input-box>
</span>
</ng-template>
@@ -237,17 +243,22 @@
<ng-template #xAxisLabelInput>
<span>
{{localizedStrings.X_AXIS_LABEL}}
<input-box (onDidChange)="setConfigValue('xAxisLabel', $event)"></input-box>
<input-box (onDidChange)="setConfigValue('xAxisLabel', $event)"
[aria-label]="localizedStrings.X_AXIS_LABEL"></input-box>
</span>
</ng-template>
<ng-template #xAxisMinMaxInput>
<span>
{{localizedStrings.X_AXIS_MAX_VAL}}
<input-box [type]="'number'" (onDidChange)="setConfigValue('xAxisMax', $event)"></input-box>
<input-box [type]="'number'"
(onDidChange)="setConfigValue('xAxisMax', $event)"
[aria-label]="localizedStrings.X_AXIS_MAX_VAL"></input-box>
</span>
<span>
{{localizedStrings.X_AXIS_MIN_VAL}}
<input-box [type]="'number'" (onDidChange)="setConfigValue('xAxisMin', $event)"></input-box>
<input-box [type]="'number'"
(onDidChange)="setConfigValue('xAxisMin', $event)"
[aria-label]="localizedStrings.X_AXIS_MIN_VAL"></input-box>
</span>
</ng-template>

View File

@@ -57,8 +57,8 @@ const LocalizedStrings = {
DATA_TYPE: nls.localize('dataTypeLabel', 'Data Type'),
NUMBER: nls.localize('numberLabel', 'Number'),
POINT: nls.localize('pointLabel', 'Point'),
LABEL_FIRST_COLUMN: nls.localize('labelFirstColumnLabel', 'Use First Column as row label?'),
COLUMNS_AS_LABELS: nls.localize('columnsAsLabelsLabel', 'Use Column names as labels?'),
LABEL_FIRST_COLUMN: nls.localize('labelFirstColumnLabel', 'Use first column as row label'),
COLUMNS_AS_LABELS: nls.localize('columnsAsLabelsLabel', 'Use column names as labels'),
LEGEND: nls.localize('legendLabel', 'Legend Position'),
CHART_NOT_FOUND: nls.localize('chartNotFound', 'Could not find chart to save'),
X_AXIS_LABEL: nls.localize('xAxisLabel', 'X Axis Label'),
@@ -122,15 +122,13 @@ export class ChartViewerComponent implements OnInit, OnDestroy, IChartViewAction
this._chartConfig = <ILineConfig>{
dataDirection: 'vertical',
dataType: 'point',
legendPosition: 'none',
labelFirstColumn: false
legendPosition: 'none'
};
} else {
this._chartConfig = <ILineConfig>{
dataDirection: 'vertical',
dataType: 'number',
legendPosition: 'none',
labelFirstColumn: false
legendPosition: 'none'
};
}
}
@@ -327,12 +325,18 @@ export class ChartViewerComponent implements OnInit, OnDestroy, IChartViewAction
@Input() set dataSet(dataSet: IGridDataSet) {
// Setup the execute result
this._executeResult = <IInsightData>{};
this._executeResult.columns = dataSet.columnDefinitions.map(def => def.name);
this._executeResult.rows = dataSet.dataRows.getRange(0, dataSet.dataRows.getLength()).map(gridRow => {
return gridRow.values.map(cell => (cell.invariantCultureDisplayValue === null || cell.invariantCultureDisplayValue === undefined) ? cell.displayValue : cell.invariantCultureDisplayValue);
// Remove first column and its value since this is the row number column
this._executeResult.columns = dataSet.columnDefinitions.slice(1).map(def => def.name);
this._executeResult.rows = dataSet.dataRows.getRange(0, dataSet.dataRows.getLength()).map(v => {
return this._executeResult.columns.reduce((p, c) => {
p.push(v[c]);
return p;
}, []);
});
}
public initChart() {
this._cd.detectChanges();
if (this._executeResult) {

View File

@@ -36,7 +36,7 @@
<a class="gridIcon" style="cursor: pointer;"
role="button"
(click)="icon.functionality(dataSet.batchId, dataSet.resultId, i)"
[title]="icon.hoverText()" [ngClass]="icon.icon()">
[title]="icon.hoverText()" [ngClass]="icon.icon()" tabindex="0">
</a>
</div>
</div>

View File

@@ -15,7 +15,7 @@ import {
ElementRef, QueryList, ChangeDetectorRef, OnInit, OnDestroy, Component, Inject,
ViewChildren, forwardRef, EventEmitter, Input, ViewChild
} from '@angular/core';
import { IGridDataRow, SlickGrid, VirtualizedCollection, ISlickRange } from 'angular2-slickgrid';
import { IGridDataRow, SlickGrid, VirtualizedCollection } from 'angular2-slickgrid';
import * as LocalizedConstants from 'sql/parts/query/common/localizedConstants';
import * as Services from 'sql/parts/grid/services/sharedServices';
@@ -163,7 +163,7 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
public onActiveCellChanged: (gridIndex: number) => void;
private savedViewState: {
gridSelections: ISlickRange[][];
gridSelections: Slick.Range[][];
resultsScroll: number;
messagePaneScroll: number;
slickGridScrolls: { vertical: number; horizontal: number }[];

View File

@@ -196,21 +196,21 @@ export class InsightsDialogView extends Modal {
}
}));
this._register(this._topTable.onContextMenu((e: DOMEvent, data: Slick.OnContextMenuEventArgs<any>) => {
this._register(this._topTable.onContextMenu(e => {
if (this.hasActions()) {
this._contextMenuService.showContextMenu({
getAnchor: () => e.target as HTMLElement,
getAnchor: () => e.anchor,
getActions: () => this.insightActions,
getActionsContext: () => this.topInsightContext(this._topTableData.getItem(this._topTable.getCellFromEvent(e).row))
getActionsContext: () => this.topInsightContext(this._topTableData.getItem(e.cell.row))
});
}
}));
this._register(this._bottomTable.onContextMenu((e: DOMEvent, data: Slick.OnContextMenuEventArgs<any>) => {
this._register(this._bottomTable.onContextMenu(e => {
this._contextMenuService.showContextMenu({
getAnchor: () => e.target as HTMLElement,
getAnchor: () => e.anchor,
getActions: () => TPromise.as([this._instantiationService.createInstance(CopyInsightDialogSelectionAction, CopyInsightDialogSelectionAction.ID, CopyInsightDialogSelectionAction.LABEL)]),
getActionsContext: () => this.bottomInsightContext(this._bottomTableData.getItem(this._bottomTable.getCellFromEvent(e).row), this._bottomTable.getCellFromEvent(e))
getActionsContext: () => this.bottomInsightContext(this._bottomTableData.getItem(e.cell.row), e.cell)
});
}));

View File

@@ -31,6 +31,10 @@ jobhistory-component {
border-bottom: 3px solid #444444;
}
.hc-black .job-heading-container {
border-bottom: 3px solid #2b56f2;
}
.jobview-grid {
height: calc(100% - 75px);
width : 100%;
@@ -43,17 +47,15 @@ jobhistory-component {
#agentViewDiv .slick-header-column {
background-color: transparent !important;
background: white !important;
border: 0px !important;
font-weight: bold;
}
.vs-dark #jobsDiv jobsview-component .jobview-grid .grid-canvas .ui-widget-content.slick-row .slick-cell {
background:#333333;
.hc-black #jobsDiv jobsview-component .jobview-grid .grid-canvas .ui-widget-content.slick-row .slick-cell {
border: 1px solid #2b56f2;
}
#jobsDiv jobsview-component .jobview-grid .grid-canvas .ui-widget-content.slick-row .slick-cell {
background: white;
border-right: transparent !important;
border-left: transparent !important;
line-height: 33px !important;
@@ -161,19 +163,12 @@ jobhistory-component {
margin-left: 20px;
}
.vs-dark #jobsDiv .dynamic-cell-detail {
background: black !important;
}
#jobsDiv .dynamic-cell-detail {
background: #faf5f8 !important;
}
.jobsview-icon {
background-image: url('./job.svg');
}
.vs-dark .jobsview-icon {
.vs-dark .jobsview-icon,
.hc-black .jobsview-icon {
background-image: url('./job_inverse.svg');
}
@@ -181,7 +176,8 @@ jobhistory-component {
background-image: url('./alert.svg');
}
.vs-dark .alertsview-icon {
.vs-dark .alertsview-icon,
.hc-black .alertsview-icon {
background-image: url('./alert_inverse.svg');
}
@@ -189,7 +185,8 @@ jobhistory-component {
background-image: url('./proxy.svg');
}
.vs-dark .proxiesview-icon {
.vs-dark .proxiesview-icon,
.hc-black .proxiesview-icon {
background-image: url('./proxy_inverse.svg');
}
@@ -197,7 +194,8 @@ jobhistory-component {
background-image: url('./operator.svg');
}
.vs-dark .operatorsview-icon {
.vs-dark .operatorsview-icon,
.hc-black .operatorsview-icon {
background-image: url('./operator_inverse.svg');
}
@@ -210,6 +208,10 @@ agentview-component .jobview-grid .grid-canvas > .ui-widget-content.slick-row.od
border-left: 1px dotted white;
}
.hc-black .jobview-grid > .monaco-table .slick-header-columns .slick-resizable-handle {
border-left: 1px dotted #2b56f2;
}
.jobview-grid > .monaco-table .slick-header-columns .slick-resizable-handle {
border-left: 1px dotted #444444;
}
@@ -238,6 +240,12 @@ agentview-component .jobview-grid .grid-canvas > .ui-widget-content.slick-row.od
background: #444444 !important;
}
.hc-black #jobsDiv .jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row:hover > .slick-cell,
.hc-black #jobsDiv .jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row > .slick-cell.hovered,
.hc-black #jobsDiv .jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row.hovered > .slick-cell {
background: none !important;
}
table.jobprevruns div.bar0, table.jobprevruns div.bar1, table.jobprevruns div.bar2,
table.jobprevruns div.bar3, table.jobprevruns div.bar4, table.jobprevruns div.bar5 {
padding-top: 3px;
@@ -258,33 +266,6 @@ table.jobprevruns > tbody {
vertical-align: bottom;
}
.jobs-view-toolbar{
display: flex;
flex-wrap: wrap;
align-items: flex-start;
padding: 15px 10px 15px 30px;
font-size: 16px;
border-bottom: 3px solid #f4f4f4;
}
.jobs-view-toolbar .vs-dark {
border-bottom: 3px solid #444444;
}
.jobs-view-toolbar > div{
display: flex;
flex-wrap: nowrap;
align-items: flex-start;
margin-right: 25px;
cursor: pointer;
padding: 2px;
}
.jobs-view-toolbar span{
padding-left: 5px;
}
#alertsDiv .jobalertsview-grid {
height: calc(100% - 75px);
width : 100%;
@@ -329,12 +310,7 @@ jobsview-component .jobview-grid .slick-cell.error-row {
opacity: 0;
}
.vs-dark #alertsDiv jobalertsview-component .jobalertsview-grid .grid-canvas .ui-widget-content.slick-row .slick-cell {
background:#333333;
}
#alertsDiv jobalertsview-component .jobalertsview-grid .grid-canvas .ui-widget-content.slick-row .slick-cell {
background: white;
border-right: transparent !important;
border-left: transparent !important;
line-height: 33px !important;
@@ -360,12 +336,7 @@ jobsview-component .jobview-grid .slick-cell.error-row {
border-left: 1px dotted #444444;
}
.vs-dark #operatorsDiv joboperatorsview-component .joboperatorsview-grid .grid-canvas .ui-widget-content.slick-row .slick-cell {
background:#333333;
}
#operatorsDiv joboperatorsview-component .joboperatorsview-grid .grid-canvas .ui-widget-content.slick-row .slick-cell {
background: white;
border-right: transparent !important;
border-left: transparent !important;
line-height: 33px !important;
@@ -391,12 +362,7 @@ jobsview-component .jobview-grid .slick-cell.error-row {
border-left: 1px dotted #444444;
}
.vs-dark #proxiesDiv jobproxiesview-component .jobproxiesview-grid .grid-canvas .ui-widget-content.slick-row .slick-cell {
background:#333333;
}
#proxiesDiv jobproxiesview-component .jobproxiesview-grid .grid-canvas .ui-widget-content.slick-row .slick-cell {
background: white;
border-right: transparent !important;
border-left: transparent !important;
line-height: 33px !important;

View File

@@ -109,10 +109,10 @@ export class AlertsViewComponent extends JobManagementView implements OnInit {
$(this._gridEl.nativeElement).empty();
$(this.actionBarContainer.nativeElement).empty();
this.initActionBar();
this._table = new Table(this._gridEl.nativeElement, undefined, columns, this.options);
this._table = new Table(this._gridEl.nativeElement, {columns}, this.options);
this._table.grid.setData(this.dataView, true);
this._register(this._table.onContextMenu((e: DOMEvent, data: Slick.OnContextMenuEventArgs<any>) => {
this._register(this._table.onContextMenu(e => {
self.openContextMenu(e);
}));

View File

@@ -28,6 +28,10 @@
color: #fff;
}
.hc-black .overview-container .overview-tab {
color: #ffffff;
}
.overview-container > .overview-tab {
position: relative;
margin-bottom: 1px;
@@ -46,6 +50,11 @@ input#accordion {
background: #444444;
}
.hc-black .overview-container .overview-tab label {
background: #000000;
border: 1px solid #2b56f2;
}
.overview-container .overview-tab label {
position: relative;
display: block;
@@ -61,6 +70,11 @@ input#accordion {
background: #333333;
}
.hc-black .overview-tab .accordion-content {
background: #000000;
border: 1px solid #2b56f2;
}
.overview-tab .accordion-content {
max-height: 0;
overflow: hidden;
@@ -105,7 +119,8 @@ input#accordion:checked ~ .accordion-content {
padding-bottom: 10px;
}
.vs-dark.monaco-shell .all-jobs >.back-button-icon {
.vs-dark.monaco-shell .all-jobs >.back-button-icon,
.hc-black.monaco-shell .all-jobs >.back-button-icon {
content: url('../common/media/back_inverse.svg');
}
@@ -166,15 +181,6 @@ table.step-list tr.step-row td {
display: flex;
}
.vs-dark .history-details > .job-steps {
display: block;
border-left: 3px solid #444444;
padding-left: 10px;
height: 100%;
width: 90%;
overflow-y: scroll;
}
.history-details > .job-steps {
display: block;
border-left: 3px solid #f4f4f4;
@@ -184,6 +190,14 @@ table.step-list tr.step-row td {
overflow-y: scroll;
}
.vs-dark .history-details > .job-steps {
border-left: 3px solid #444444;
}
.hc-black .history-details > .job-steps {
border-left: 3px solid #2b56f2;
}
.history-details > .job-steps > .step-list {
padding-bottom: 10px;
}
@@ -251,3 +265,6 @@ jobhistory-component > .actionbar-container .monaco-action-bar > ul.actions-cont
border-top: 3px solid #444444;
}
.hc-black jobhistory-component > .actionbar-container .monaco-action-bar > ul.actions-container {
border-top: 3px solid #2b56f2;
}

View File

@@ -74,6 +74,7 @@ export class JobStepsViewComponent extends JobManagementView implements OnInit,
}
ngOnInit() {
let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri;
this._tree = new Tree(this._tableContainer.nativeElement, {
controller: this._treeController,

View File

@@ -27,7 +27,6 @@ import { HeaderFilter } from 'sql/base/browser/ui/table/plugins/headerFilter.plu
import { IJobManagementService } from 'sql/parts/jobManagement/common/interfaces';
import { JobManagementView } from 'sql/parts/jobManagement/views/jobManagementView';
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
@@ -36,6 +35,8 @@ import { IAction } from 'vs/base/common/actions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IDashboardService } from 'sql/services/dashboard/common/dashboardService';
import { escape } from 'sql/base/common/strings';
import { IWorkbenchThemeService, IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { tableBackground, cellBackground, tableHoverBackground, jobsHeadingBackground, cellBorderColor } from 'sql/common/theme/colors';
export const JOBSVIEW_SELECTOR: string = 'jobsview-component';
export const ROW_HEIGHT: number = 45;
@@ -96,7 +97,7 @@ export class JobsViewComponent extends JobManagementView implements OnInit {
@Inject(forwardRef(() => ElementRef)) private _el: ElementRef,
@Inject(forwardRef(() => AgentViewComponent)) private _agentViewComponent: AgentViewComponent,
@Inject(IJobManagementService) private _jobManagementService: IJobManagementService,
@Inject(IThemeService) private _themeService: IThemeService,
@Inject(IWorkbenchThemeService) private _themeService: IWorkbenchThemeService,
@Inject(ICommandService) private _commandService: ICommandService,
@Inject(IInstantiationService) instantiationService: IInstantiationService,
@Inject(IContextMenuService) contextMenuService: IContextMenuService,
@@ -121,6 +122,7 @@ export class JobsViewComponent extends JobManagementView implements OnInit {
// set base class elements
this._visibilityElement = this._gridEl;
this._parentComponent = this._agentViewComponent;
this._register(this._themeService.onDidColorThemeChange(e => this.updateTheme(e)));
}
public layout() {
@@ -175,7 +177,7 @@ export class JobsViewComponent extends JobManagementView implements OnInit {
$(this._gridEl.nativeElement).empty();
$(this.actionBarContainer.nativeElement).empty();
this.initActionBar();
this._table = new Table(this._gridEl.nativeElement, undefined, columns, options);
this._table = new Table(this._gridEl.nativeElement, {columns}, options);
this._table.grid.setData(this.dataView, true);
this._table.grid.onClick.subscribe((e, args) => {
let job = self.getJob(args);
@@ -184,7 +186,7 @@ export class JobsViewComponent extends JobManagementView implements OnInit {
self._agentViewComponent.showHistory = true;
});
this._register(this._table.onContextMenu((e: DOMEvent, data: Slick.OnContextMenuEventArgs<any>) => {
this._register(this._table.onContextMenu(e => {
self.openContextMenu(e);
}));
@@ -820,6 +822,25 @@ export class JobsViewComponent extends JobManagementView implements OnInit {
}
}
private updateTheme(theme: IColorTheme) {
let bgColor = theme.getColor(tableBackground);
let cellColor = theme.getColor(cellBackground);
let borderColor = theme.getColor(cellBorderColor);
let headerColumns = $('#agentViewDiv .slick-header-column');
let cells = $('.grid-canvas .ui-widget-content.slick-row .slick-cell');
let cellDetails = $('#jobsDiv .dynamic-cell-detail');
headerColumns.toArray().forEach(col => {
col.style.background = bgColor.toString();
});
cells.toArray().forEach(cell => {
cell.style.background = bgColor.toString();
cell.style.border = borderColor ? '1px solid ' + borderColor.toString() : null;
});
cellDetails.toArray().forEach(cellDetail => {
cellDetail.style.background = cellColor.toString();
});
}
protected getTableActions(): TPromise<IAction[]> {
let actions: IAction[] = [];
actions.push(this._instantiationService.createInstance(EditJobAction));

View File

@@ -110,10 +110,10 @@ export class OperatorsViewComponent extends JobManagementView implements OnInit
$(this._gridEl.nativeElement).empty();
$(this.actionBarContainer.nativeElement).empty();
this.initActionBar();
this._table = new Table(this._gridEl.nativeElement, undefined, columns, this.options);
this._table = new Table(this._gridEl.nativeElement, {columns}, this.options);
this._table.grid.setData(this.dataView, true);
this._register(this._table.onContextMenu((e: DOMEvent, data: Slick.OnContextMenuEventArgs<any>) => {
this._register(this._table.onContextMenu(e => {
self.openContextMenu(e);
}));

View File

@@ -112,10 +112,10 @@ export class ProxiesViewComponent extends JobManagementView implements OnInit {
$(this._gridEl.nativeElement).empty();
$(this.actionBarContainer.nativeElement).empty();
this.initActionBar();
this._table = new Table(this._gridEl.nativeElement, undefined, columns, this.options);
this._table = new Table(this._gridEl.nativeElement, {columns}, this.options);
this._table.grid.setData(this.dataView, true);
this._register(this._table.onContextMenu((e: DOMEvent, data: Slick.OnContextMenuEventArgs<any>) => {
this._register(this._table.onContextMenu(e => {
self.openContextMenu(e);
}));

View File

@@ -32,18 +32,15 @@ export enum DeclarativeDataType {
@Component({
selector: 'modelview-declarativeTable',
template: `
<table role=grid aria-labelledby="ID_REF" #container *ngIf="columns" class="declarative-table">
<table role=grid aria-labelledby="ID_REF" #container *ngIf="columns" class="declarative-table" [style.height]="getHeight()">
<thead>
<tr style="display:block;">
<ng-container *ngFor="let column of columns;let h = index">
<td class="declarative-table-header" tabindex="-1" [style.width]="getColumnWidth(h)" role="button" aria-sort="none">{{column.displayName}}</td>
<th class="declarative-table-header" tabindex="-1" role="button" aria-sort="none">{{column.displayName}}</th>
</ng-container>
</tr>
</thead>
<ng-container *ngIf="data">
<tbody style="display: block; width:100%; overflow-y: scroll" [style.height]="getHeight()">
<ng-container *ngFor="let row of data;let r = index">
<tr class="declarative-table-row">
<tr class="declarative-table-row" >
<ng-container *ngFor="let cellData of row;let c = index">
<td class="declarative-table-cell" tabindex="-1" role="button" [style.width]="getColumnWidth(c)">
<checkbox *ngIf="isCheckBox(c)" label="" (onChange)="onCheckBoxChanged($event,r,c)" [enabled]="isControlEnabled(c)" [checked]="isChecked(r,c)"></checkbox>
@@ -54,9 +51,7 @@ export enum DeclarativeDataType {
</td>
</ng-container>
</tr>
</ng-container>
</tbody>
</ng-container>
</table>
`

View File

@@ -33,7 +33,7 @@ export default class EditorComponent extends ComponentBase implements IComponent
private _editorInput: UntitledEditorInput;
private _editorModel: ITextModel;
private _renderedContent: string;
private _langaugeMode: string;
private _languageMode: string;
constructor(
@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef,
@@ -76,10 +76,13 @@ export default class EditorComponent extends ComponentBase implements IComponent
public layout(): void {
let width: number = this.convertSizeToNumber(this.width);
let height: number = this.convertSizeToNumber(this.height);
this._editor.layout(new DOM.Dimension(
width && width > 0 ? width : DOM.getContentWidth(this._el.nativeElement),
height && height > 0 ? height : DOM.getContentHeight(this._el.nativeElement)));
let element = <HTMLElement> this._el.nativeElement;
element.style.position = this.position;
}
/// Editor Functions
@@ -92,8 +95,8 @@ export default class EditorComponent extends ComponentBase implements IComponent
private updateLanguageMode() {
if (this._editorModel && this._editor) {
this._langaugeMode = this.languageMode;
this._modeService.getOrCreateMode(this._langaugeMode).then((modeValue) => {
this._languageMode = this.languageMode;
this._modeService.getOrCreateMode(this._languageMode).then((modeValue) => {
this._modelService.setMode(this._editorModel, modeValue);
});
}
@@ -111,7 +114,7 @@ export default class EditorComponent extends ComponentBase implements IComponent
if (this.content !== this._renderedContent) {
this.updateModel();
}
if (this.languageMode !== this._langaugeMode) {
if (this.languageMode !== this._languageMode) {
this.updateLanguageMode();
}
}
@@ -132,4 +135,12 @@ export default class EditorComponent extends ComponentBase implements IComponent
public set languageMode(newValue: string) {
this.setPropertyFromUI<sqlops.EditorProperties, string>((properties, languageMode) => { properties.languageMode = languageMode; }, newValue);
}
public get position(): string {
return this.getPropertyOrDefault<sqlops.EditorProperties, string>((props) => props.position, '');
}
public set position(newValue: string) {
this.setPropertyFromUI<sqlops.EditorProperties, string>((properties, position) => { properties.languoffsetMarginageMode = position; }, newValue);
}
}

View File

@@ -28,12 +28,13 @@ import { generateUuid } from 'vs/base/common/uuid';
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
import { Event, Emitter } from 'vs/base/common/event';
import * as nls from 'vs/nls';
import { LayoutRequestParams } from 'sql/platform/dialog/dialogContainer.component';
const componentRegistry = <IComponentRegistry>Registry.as(Extensions.ComponentContribution);
export interface ModelComponentParams extends IBootstrapParams {
onLayoutRequested: Event<string>;
onLayoutRequested: Event<LayoutRequestParams>;
modelViewId: string;
}
@@ -69,8 +70,8 @@ export class ModelComponentWrapper extends AngularDisposable implements OnInit {
super();
if (_params && _params.onLayoutRequested) {
this._modelViewId = _params.modelViewId;
_params.onLayoutRequested(modelViewId => {
if (modelViewId === this._modelViewId) {
_params.onLayoutRequested(layoutParams => {
if (layoutParams && (layoutParams.alwaysRefresh || layoutParams.modelViewId === this._modelViewId)) {
this.layout();
}
});

View File

@@ -79,7 +79,8 @@ export class ModelViewEditor extends BaseEditor {
}
private doUpdateContainer() {
const modelViewContainer = this.input && (this.input as ModelViewInput).container;
let modelViewInput = this.input as ModelViewInput;
const modelViewContainer = modelViewInput && modelViewInput.container;
if (modelViewContainer) {
const frameRect = this._editorFrame.getBoundingClientRect();
const containerRect = modelViewContainer.parentElement.getBoundingClientRect();
@@ -89,6 +90,9 @@ export class ModelViewEditor extends BaseEditor {
modelViewContainer.style.left = `${frameRect.left - containerRect.left}px`;
modelViewContainer.style.width = `${frameRect.width}px`;
modelViewContainer.style.height = `${frameRect.height}px`;
if (modelViewInput.dialogPane) {
modelViewInput.dialogPane.layout(true);
}
}
}

View File

@@ -25,7 +25,7 @@ import * as sqlops from 'sqlops';
@Component({
selector: 'modelview-content',
template: `
<div *ngIf="rootDescriptor" style="width: 100%; height: 100%; position: absolute;">
<div *ngIf="rootDescriptor" style="width: 100%; height: 100%;">
<model-component-wrapper style="display: block; height: 100%" [descriptor]="rootDescriptor" [modelStore]="modelStore">
</model-component-wrapper>
</div>

View File

@@ -24,16 +24,16 @@ import { EditorOptions } from 'vs/workbench/common/editor';
import { CodeEditor } from 'vs/editor/browser/codeEditor';
import { IEditorContributionCtor } from 'vs/editor/browser/editorExtensions';
import { FoldingController } from 'vs/editor/contrib/folding/folding';
import { RenameController } from 'vs/editor/contrib/rename/rename';
class QueryCodeEditor extends CodeEditor {
protected _getContributions(): IEditorContributionCtor[] {
let contributions = super._getContributions();
let skipContributions = [FoldingController.prototype];
let skipContributions = [FoldingController.prototype, RenameController.prototype];
contributions = contributions.filter(c => skipContributions.indexOf(c.prototype) === -1);
return contributions;
}
}
/**

View File

@@ -101,7 +101,7 @@ export default class TableComponent extends ComponentBase implements IComponent,
forceFitColumns: true
};
this._table = new Table<Slick.SlickData>(this._inputContainer.nativeElement, this._tableData, this._tableColumns, options);
this._table = new Table<Slick.SlickData>(this._inputContainer.nativeElement, { dataProvider: this._tableData, columns: this._tableColumns }, options);
this._table.setData(this._tableData);
this._table.setSelectionModel(new RowSelectionModel({ selectActiveRow: true }));

View File

@@ -5,35 +5,35 @@
import 'vs/css!./toolbarLayout';
import {
Component, Input, Inject, ChangeDetectorRef, forwardRef, ComponentFactoryResolver,
ViewChild, ViewChildren, ElementRef, Injector, OnDestroy, QueryList, AfterViewInit
Component, Input, Inject, ChangeDetectorRef, forwardRef,
ViewChild, ElementRef, OnDestroy, AfterViewInit
} from '@angular/core';
import { IComponent, IComponentDescriptor, IModelStore, ComponentEventType } from 'sql/parts/modelComponents/interfaces';
import { Orientation, ToolbarLayout } from 'sql/workbench/api/common/sqlExtHostTypes';
import { IComponent, IComponentDescriptor, IModelStore } from 'sql/parts/modelComponents/interfaces';
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
import { ContainerBase } from 'sql/parts/modelComponents/componentBase';
import { ModelComponentWrapper } from 'sql/parts/modelComponents/modelComponentWrapper.component';
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
export interface ToolbarItemConfig {
title?: string;
}
class ToolbarItem {
export class ToolbarItem {
constructor(public descriptor: IComponentDescriptor, public config: ToolbarItemConfig) { }
}
@Component({
selector: 'modelview-toolbarContainer',
template: `
<div #container *ngIf="items" class="modelview-toolbar-container">
<div #container *ngIf="items" [class]="toolbarClass" >
<ng-container *ngFor="let item of items">
<div class="modelview-toolbar-item" >
<div *ngIf="hasTitle(item)" class="modelview-toolbar-title" >
<div class="modelview-toolbar-item" [title]="getItemTitle(item)" [style.paddingTop]="paddingTop" tabindex="0">
<div *ngIf="shouldShowTitle(item)" class="modelview-toolbar-title" >
{{getItemTitle(item)}}
</div>
<div class="modelview-toolbar-component">
<div class="modelview-toolbar-component">
<model-component-wrapper [descriptor]="item.descriptor" [modelStore]="modelStore" >
</model-component-wrapper>
</div>
@@ -48,10 +48,13 @@ export default class ToolbarContainer extends ContainerBase<ToolbarItemConfig> i
@ViewChild('container', { read: ElementRef }) private _container: ElementRef;
private _orientation: Orientation;
constructor(
@Inject(forwardRef(() => CommonServiceInterface)) private _commonService: CommonServiceInterface,
@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef) {
super(changeRef);
this._orientation = Orientation.Horizontal;
}
ngOnInit(): void {
@@ -67,16 +70,39 @@ export default class ToolbarContainer extends ContainerBase<ToolbarItemConfig> i
/// IComponent implementation
public setLayout(layout: any): void {
public setLayout(layout: ToolbarLayout): void {
this._orientation = layout.orientation ? layout.orientation : Orientation.Horizontal;
this.layout();
}
private getItemTitle(item: ToolbarItem): string {
public getItemTitle(item: ToolbarItem): string {
let itemConfig = item.config;
return itemConfig ? itemConfig.title : '';
}
public shouldShowTitle(item: ToolbarItem): boolean {
return this.hasTitle(item) && this.isHorizontal();
}
private hasTitle(item: ToolbarItem): boolean {
return item && item.config && item.config.title !== undefined;
}
public get paddingTop(): string {
return this.isHorizontal() ? '' : '10px';
}
public get toolbarClass(): string {
let classes = ['modelview-toolbar-container'];
if (this.isHorizontal()) {
classes.push('toolbar-horizontal');
} else {
classes.push('toolbar-vertical');
}
return classes.join(' ');
}
private isHorizontal(): boolean {
return this._orientation === Orientation.Horizontal;
}
}

View File

@@ -5,17 +5,24 @@
.modelview-toolbar-container {
display: flex;
padding: 15px;
justify-content: flex-start;
line-height: 1.4em;
white-space: nowrap;
flex-wrap: wrap;
}
.modelview-toolbar-container.toolbar-horizontal {
flex-direction: row;
border-bottom-width: .5px;
border-bottom-style: solid;
box-sizing: border-box;
border-bottom-color: rgba(128, 128, 128, 0.35);
}
.modelview-toolbar-container.toolbar-vertical {
flex-direction: column;
}
.modelview-toolbar-container .modelview-toolbar-item {
flex: 0 0;
flex-direction: row;

View File

@@ -68,12 +68,16 @@ export class ServerTreeDragAndDrop implements IDragAndDrop {
let canDragOver: boolean = true;
if (targetElement instanceof ConnectionProfile || targetElement instanceof ConnectionProfileGroup) {
let targetConnectionProfileGroup = this.getTargetGroup(targetElement);
//Verify if the connection can be moved to the target group
// Verify if the connection can be moved to the target group
const source = data.getData()[0];
if (source instanceof ConnectionProfile) {
if (!this._connectionManagementService.canChangeConnectionConfig(source, targetConnectionProfileGroup.id)) {
canDragOver = false;
}
} else if (source instanceof ConnectionProfileGroup) {
// Dropping a group to itself or its descendants nodes is not allowed
// to avoid creating a circular structure.
canDragOver = source.id !== targetElement.id && !source.isAncestorOf(targetElement);
}
} else {
@@ -148,7 +152,7 @@ export class ServerTreeDragAndDrop implements IDragAndDrop {
*/
export class RecentConnectionsDragAndDrop implements IDragAndDrop {
constructor( @IConnectionManagementService private connectionManagementService: IConnectionManagementService,
constructor(@IConnectionManagementService private connectionManagementService: IConnectionManagementService,
@IInstantiationService private instantiationService: IInstantiationService
) {
}

View File

@@ -17,7 +17,6 @@ import * as Actions from 'sql/parts/profiler/contrib/profilerActions';
import { CONTEXT_PROFILER_EDITOR, PROFILER_TABLE_COMMAND_SEARCH } from './interfaces';
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox';
import { textFormatter } from 'sql/parts/grid/services/sharedServices';
import * as DOM from 'vs/base/browser/dom';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { TPromise } from 'vs/base/common/winjs.base';
@@ -99,7 +98,8 @@ export interface IDetailData {
}
export class ProfilerEditor extends BaseEditor {
public static ID: string = 'workbench.editor.profiler';
public static readonly ID: string = 'workbench.editor.profiler';
private _editor: ProfilerResourceEditor;
private _editorModel: ITextModel;
private _editorInput: UntitledEditorInput;
@@ -158,9 +158,6 @@ export class ProfilerEditor extends BaseEditor {
}
protected createEditor(parent: HTMLElement): void {
// test backend
//this._profilerService.registerProvider('default', this._instantiationService.createInstance(ProfilerTestBackend));
this._container = document.createElement('div');
this._container.className = 'carbon-profiler';
parent.appendChild(this._container);
@@ -327,7 +324,7 @@ export class ProfilerEditor extends BaseEditor {
detailTableContainer.style.width = '100%';
detailTableContainer.style.height = '100%';
this._detailTableData = new TableDataView<IDetailData>();
this._detailTable = new Table(detailTableContainer, this._detailTableData, [
this._detailTable = new Table(detailTableContainer, { dataProvider: this._detailTableData, columns: [
{
id: 'label',
name: nls.localize('label', "Label"),
@@ -340,7 +337,7 @@ export class ProfilerEditor extends BaseEditor {
field: 'value',
formatter: textFormatter
}
], { forceFitColumns: true });
]}, { forceFitColumns: true });
this._tabbedPanel.pushTab({
identifier: 'detailTable',
@@ -457,13 +454,32 @@ export class ProfilerEditor extends BaseEditor {
this._connectAction.connected = this.input.state.isConnected;
if (this.input.state.isConnected) {
this._updateToolbar();
this._sessionSelector.enable();
this._profilerService.getXEventSessions(this.input.id).then((r) => {
// set undefined result to empty list
if (!r) {
r = [];
}
this._sessionSelector.setOptions(r);
this._sessionsList = r;
if (this.input.sessionName === undefined || this.input.sessionName === '') {
this.input.sessionName = this._sessionsList[0];
if ((this.input.sessionName === undefined || this.input.sessionName === '') && this._sessionsList.length > 0) {
let sessionIndex: number = 0;
let uiState = this._profilerService.getSessionViewState(this.input.id);
if (uiState && uiState.previousSessionName) {
sessionIndex = this._sessionsList.indexOf(uiState.previousSessionName);
} else {
this._profilerService.launchCreateSessionDialog(this.input);
}
if (sessionIndex < 0) {
sessionIndex = 0;
}
this.input.sessionName = this._sessionsList[sessionIndex];
this._sessionSelector.selectWithOptionName(this.input.sessionName);
}
});
} else {
@@ -493,9 +509,14 @@ export class ProfilerEditor extends BaseEditor {
this._updateToolbar();
this._sessionSelector.enable();
this._profilerService.getXEventSessions(this.input.id).then((r) => {
// set undefined result to empty list
if (!r) {
r = [];
}
this._sessionsList = r;
this._sessionSelector.setOptions(r);
if (this.input.sessionName === undefined || this.input.sessionName === '') {
if ((this.input.sessionName === undefined || this.input.sessionName === '') && this._sessionsList.length > 0) {
this.input.sessionName = this._sessionsList[0];
}
});

View File

@@ -109,6 +109,12 @@ export interface IProfilerService {
* otherwise returns all session templates
*/
getSessionTemplates(providerId?: string): Array<IProfilerSessionTemplate>;
/**
* Gets the session view state
* @param sessionId The session ID to get the view state for
* @returns Sessions view state
*/
getSessionViewState(sessionId: string): any;
/**
* Launches the dialog for editing the view columns of a profiler session template for the given input
* @param input input object that contains the necessary information which will be modified based on used input

View File

@@ -19,6 +19,8 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { Scope as MementoScope, Memento } from 'vs/workbench/common/memento';
class TwoWayMap<T, K> {
private forwardMap: Map<T, K>;
@@ -45,19 +47,27 @@ class TwoWayMap<T, K> {
}
export class ProfilerService implements IProfilerService {
private static readonly PROFILER_SERVICE_UI_STATE_STORAGE_KEY = 'profileservice.uiState';
public _serviceBrand: any;
private _providers = new Map<string, sqlops.ProfilerProvider>();
private _idMap = new TwoWayMap<ProfilerSessionID, string>();
private _sessionMap = new Map<ProfilerSessionID, IProfilerSession>();
private _connectionMap = new Map<ProfilerSessionID, IConnectionProfile>();
private _editColumnDialog: ProfilerColumnEditorDialog;
private _memento: any;
private _context: Memento;
constructor(
@IConnectionManagementService private _connectionService: IConnectionManagementService,
@IConfigurationService public _configurationService: IConfigurationService,
@IInstantiationService private _instantiationService: IInstantiationService,
@INotificationService private _notificationService: INotificationService,
@ICommandService private _commandService: ICommandService
) { }
@ICommandService private _commandService: ICommandService,
@IStorageService private _storageService: IStorageService
) {
this._context = new Memento('ProfilerEditor');
this._memento = this._context.getMemento(this._storageService, MementoScope.GLOBAL);
}
public registerProvider(providerId: string, provider: sqlops.ProfilerProvider): void {
this._providers.set(providerId, provider);
@@ -77,23 +87,22 @@ export class ProfilerService implements IProfilerService {
}
this._sessionMap.set(uri, session);
this._connectionMap.set(uri, connectionProfile);
this._idMap.set(uri, uri);
return TPromise.wrap(uri);
}
public onMoreRows(params: sqlops.ProfilerSessionEvents): void {
this._sessionMap.get(this._idMap.reverseGet(params.sessionId)).onMoreRows(params);
}
public onSessionStopped(params: sqlops.ProfilerSessionStoppedParams): void {
this._sessionMap.get(this._idMap.reverseGet(params.ownerUri)).onSessionStopped(params);
}
public onProfilerSessionCreated(params: sqlops.ProfilerSessionCreatedParams): void {
this._sessionMap.get(this._idMap.reverseGet(params.ownerUri)).onProfilerSessionCreated(params);
this.updateMemento(params.ownerUri, { previousSessionName: params.sessionName });
}
public connectSession(id: ProfilerSessionID): Thenable<boolean> {
@@ -114,6 +123,7 @@ export class ProfilerService implements IProfilerService {
}
public startSession(id: ProfilerSessionID, sessionName: string): Thenable<boolean> {
this.updateMemento(id, { previousSessionName: sessionName });
return this._runAction(id, provider => provider.startSession(this._idMap.get(id), sessionName)).then(() => {
this._sessionMap.get(this._idMap.reverseGet(id)).onSessionStateChanged({ isRunning: true, isStopped: false, isPaused: false });
return true;
@@ -178,6 +188,35 @@ export class ProfilerService implements IProfilerService {
}
}
public getSessionViewState(ownerUri: string): any {
let mementoKey = this.getMementoKey(ownerUri);
let uiStateMap = this._memento[ProfilerService.PROFILER_SERVICE_UI_STATE_STORAGE_KEY];
if (uiStateMap && mementoKey) {
return uiStateMap[mementoKey];
}
return undefined;
}
private getMementoKey(ownerUri: string): string {
let mementoKey = undefined;
let connectionProfile: IConnectionProfile = this._connectionMap.get(ownerUri);
if (connectionProfile) {
mementoKey = connectionProfile.serverName;
}
return mementoKey;
}
private updateMemento(ownerUri: string, uiState: any) {
// update persisted session state
let mementoKey = this.getMementoKey(ownerUri);
let uiStateMap = this._memento[ProfilerService.PROFILER_SERVICE_UI_STATE_STORAGE_KEY];
if (uiStateMap && mementoKey) {
uiStateMap[mementoKey] = uiState;
this._memento[ProfilerService.PROFILER_SERVICE_UI_STATE_STORAGE_KEY] = uiStateMap;
this._context.saveMemento();
}
}
public launchColumnEditor(input?: ProfilerInput): Thenable<void> {
if (!this._editColumnDialog) {
this._editColumnDialog = this._instantiationService.createInstance(ProfilerColumnEditorDialog);

View File

@@ -26,10 +26,11 @@ import * as paths from 'vs/base/common/paths';
import * as nls from 'vs/nls';
import * as pretty from 'pretty-data';
import { ISlickRange } from 'angular2-slickgrid';
import * as path from 'path';
import Severity from 'vs/base/common/severity';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { getBaseLabel } from 'vs/base/common/labels';
import { ShowFileInFolderAction, OpenFileInFolderAction } from 'sql/workbench/common/workspaceActions';
let prevSavePath: string;
@@ -253,7 +254,7 @@ export class ResultSerializer {
return config;
}
private getParameters(filePath: string, batchIndex: number, resultSetNo: number, format: string, selection: ISlickRange): SaveResultsRequestParams {
private getParameters(filePath: string, batchIndex: number, resultSetNo: number, format: string, selection: Slick.Range): SaveResultsRequestParams {
let saveResultsParams: SaveResultsRequestParams;
if (!path.isAbsolute(filePath)) {
this._filePath = PathUtilities.resolveFilePath(this._uri, filePath, this.rootPath);
@@ -285,14 +286,39 @@ export class ResultSerializer {
/**
* Check if a range of cells were selected.
*/
private isSelected(selection: ISlickRange): boolean {
private isSelected(selection: Slick.Range): boolean {
return (selection && !((selection.fromCell === selection.toCell) && (selection.fromRow === selection.toRow)));
}
private promptFileSavedNotification(savedFilePath: string) {
let label = getBaseLabel(paths.dirname(savedFilePath));
this._notificationService.prompt(
Severity.Info,
LocalizedConstants.msgSaveSucceeded + savedFilePath,
[{
label: nls.localize('openLocation', "Open file location"),
run: () => {
let action = new ShowFileInFolderAction(savedFilePath, label || paths.sep, this._windowsService);
action.run();
action.dispose();
}
}, {
label: nls.localize('openFile', "Open file"),
run: () => {
let action = new OpenFileInFolderAction(savedFilePath, label || paths.sep, this._windowsService);
action.run();
action.dispose();
}
}]
);
}
/**
* Send request to sql tools service to save a result set
*/
private sendRequestToService(filePath: string, batchIndex: number, resultSetNo: number, format: string, selection: ISlickRange): Thenable<void> {
private sendRequestToService(filePath: string, batchIndex: number, resultSetNo: number, format: string, selection: Slick.Range): Thenable<void> {
let saveResultsParams = this.getParameters(filePath, batchIndex, resultSetNo, format, selection);
this.logToOutputChannel(LocalizedConstants.msgSaveStarted + this._filePath);
@@ -306,10 +332,7 @@ export class ResultSerializer {
});
this.logToOutputChannel(LocalizedConstants.msgSaveFailed + result.messages);
} else {
this._notificationService.notify({
severity: Severity.Info,
message: LocalizedConstants.msgSaveSucceeded + this._filePath
});
this.promptFileSavedNotification(this._filePath);
this.logToOutputChannel(LocalizedConstants.msgSaveSucceeded + filePath);
this.openSavedFile(this._filePath, format);
}
@@ -329,13 +352,7 @@ export class ResultSerializer {
* Open the saved file in a new vscode editor pane
*/
private openSavedFile(filePath: string, format: string): void {
if (format === SaveFormat.EXCEL) {
// This will not open in VSCode as it's treated as binary. Use the native file opener instead
// Note: must use filePath here, URI does not open correctly
// TODO see if there is an alternative opener that includes error handling
let fileUri = URI.from({ scheme: PathUtilities.FILE_SCHEMA, path: filePath });
this._windowsService.openExternal(fileUri.toString());
} else {
if (format !== SaveFormat.EXCEL) {
let uri = URI.file(filePath);
this._editorService.openEditor({ resource: uri }).then((result) => {

View File

@@ -0,0 +1,160 @@
/*---------------------------------------------------------------------------------------------
* 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 { Action } from 'vs/base/common/actions';
import { localize } from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { ITree } from 'vs/base/parts/tree/browser/tree';
import QueryRunner from 'sql/parts/query/execution/queryRunner';
import { SaveFormat } from 'sql/parts/grid/common/interfaces';
import { Table } from 'sql/base/browser/ui/table/table';
import { GridTableState } from 'sql/parts/query/editor/gridPanel';
export interface IGridActionContext {
cell: { row: number; cell: number; };
selection: Slick.Range[];
runner: QueryRunner;
batchId: number;
resultId: number;
table: Table<any>;
tableState: GridTableState;
}
export interface IMessagesActionContext {
selection: Selection;
tree: ITree;
}
export class SaveResultAction extends Action {
public static SAVECSV_ID = 'grid.saveAsCsv';
public static SAVECSV_LABEL = localize('saveAsCsv', 'Save As CSV');
public static SAVECSV_ICON = 'saveCsv';
public static SAVEJSON_ID = 'grid.saveAsJson';
public static SAVEJSON_LABEL = localize('saveAsJson', 'Save As JSON');
public static SAVEJSON_ICON = 'saveJson';
public static SAVEEXCEL_ID = 'grid.saveAsExcel';
public static SAVEEXCEL_LABEL = localize('saveAsExcel', 'Save As Excel');
public static SAVEEXCEL_ICON = 'saveExcel';
constructor(
id: string,
label: string,
icon: string,
private format: SaveFormat
) {
super(id, label, icon);
}
public run(context: IGridActionContext): TPromise<boolean> {
context.runner.serializeResults(context.batchId, context.resultId, this.format, context.selection);
return TPromise.as(true);
}
}
export class CopyResultAction extends Action {
public static COPY_ID = 'grid.copySelection';
public static COPY_LABEL = localize('copySelection', 'Copy');
public static COPYWITHHEADERS_ID = 'grid.copyWithHeaders';
public static COPYWITHHEADERS_LABEL = localize('copyWithHeaders', 'Copy With Headers');
constructor(
id: string,
label: string,
private copyHeader: boolean,
) {
super(id, label);
}
public run(context: IGridActionContext): TPromise<boolean> {
context.runner.copyResults(context.selection, context.batchId, context.resultId, this.copyHeader);
return TPromise.as(true);
}
}
export class SelectAllGridAction extends Action {
public static ID = 'grid.selectAll';
public static LABEL = localize('selectAll', 'Select All');
constructor() {
super(SelectAllGridAction.ID, SelectAllGridAction.LABEL);
}
public run(context: IGridActionContext): TPromise<boolean> {
context.table.setSelectedRows(true);
return TPromise.as(true);
}
}
export class CopyMessagesAction extends Action {
public static ID = 'grid.messages.copy';
public static LABEL = localize('copyMessages', 'Copy');
constructor(
@IClipboardService private clipboardService: IClipboardService
) {
super(CopyMessagesAction.ID, CopyMessagesAction.LABEL);
}
public run(context: IMessagesActionContext): TPromise<boolean> {
this.clipboardService.writeText(context.selection.toString());
return TPromise.as(true);
}
}
export class SelectAllMessagesAction extends Action {
public static ID = 'grid.messages.selectAll';
public static LABEL = localize('selectAll', 'Select All');
constructor() {
super(SelectAllMessagesAction.ID, SelectAllMessagesAction.LABEL);
}
public run(context: IMessagesActionContext): TPromise<boolean> {
let range = document.createRange();
range.selectNodeContents(context.tree.getHTMLElement());
let sel = document.getSelection();
sel.removeAllRanges();
sel.addRange(range);
return TPromise.as(true);
}
}
export class MaximizeTableAction extends Action {
public static ID = 'grid.maximize';
public static LABEL = localize('maximize', 'Maximize');
public static ICON = 'extendFullScreen';
constructor() {
super(MaximizeTableAction.ID, MaximizeTableAction.LABEL, MaximizeTableAction.ICON);
}
public run(context: IGridActionContext): TPromise<boolean> {
context.tableState.maximized = true;
return TPromise.as(true);
}
}
export class MinimizeTableAction extends Action {
public static ID = 'grid.minimize';
public static LABEL = localize('minimize', 'Minimize');
public static ICON = 'exitFullScreen';
constructor() {
super(MinimizeTableAction.ID, MinimizeTableAction.LABEL, MinimizeTableAction.ICON);
}
public run(context: IGridActionContext): TPromise<boolean> {
context.tableState.maximized = false;
return TPromise.as(true);
}
}

View File

@@ -0,0 +1,414 @@
/*---------------------------------------------------------------------------------------------
* 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 { attachTableStyler } from 'sql/common/theme/styler';
import QueryRunner from 'sql/parts/query/execution/queryRunner';
import { VirtualizedCollection, AsyncDataProvider } from 'sql/base/browser/ui/table/asyncDataView';
import { Table, ITableStyles, ITableContextMenuEvent } from 'sql/base/browser/ui/table/table';
import { ScrollableSplitView } from 'sql/base/browser/ui/scrollableSplitview/scrollableSplitview';
import { MouseWheelSupport } from 'sql/base/browser/ui/table/plugins/mousewheelTableScroll.plugin';
import { AutoColumnSize } from 'sql/base/browser/ui/table/plugins/autoSizeColumns.plugin';
import { SaveFormat } from 'sql/parts/grid/common/interfaces';
import { IGridActionContext, SaveResultAction, CopyResultAction, SelectAllGridAction, MaximizeTableAction, MinimizeTableAction } from 'sql/parts/query/editor/actions';
import { CellSelectionModel } from 'sql/base/browser/ui/table/plugins/cellSelectionModel.plugin';
import { RowNumberColumn } from 'sql/base/browser/ui/table/plugins/rowNumberColumn.plugin';
import * as sqlops from 'sqlops';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { Emitter, Event } from 'vs/base/common/event';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { ViewletPanel, IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet';
import { isUndefinedOrNull } from 'vs/base/common/types';
import { range } from 'vs/base/common/arrays';
import { Orientation, IView } from 'vs/base/browser/ui/splitview/splitview';
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
import { $ } from 'vs/base/browser/builder';
import { generateUuid } from 'vs/base/common/uuid';
import { TPromise } from 'vs/base/common/winjs.base';
import { Separator, ActionBar, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
import { Dimension, getContentWidth } from 'vs/base/browser/dom';
const rowHeight = 29;
const columnHeight = 26;
const minGridHeightInRows = 8;
const estimatedScrollBarHeight = 10;
export interface IGridTableState {
canBeMaximized: boolean;
maximized: boolean;
}
export class GridTableState {
private _maximized: boolean;
private _onMaximizedChange = new Emitter<boolean>();
public onMaximizedChange: Event<boolean> = this._onMaximizedChange.event;
public canBeMaximized: boolean;
constructor(state?: IGridTableState) {
if (state) {
this._maximized = state.maximized;
this.canBeMaximized = state.canBeMaximized;
}
}
public get maximized(): boolean {
return this._maximized;
}
public set maximized(val: boolean) {
if (val === this._maximized) {
return;
}
this._maximized = val;
this._onMaximizedChange.fire(val);
}
public clone(): GridTableState {
return new GridTableState({ canBeMaximized: this.canBeMaximized, maximized: this.maximized });
}
}
export class GridPanel extends ViewletPanel {
private container = document.createElement('div');
private splitView: ScrollableSplitView;
private tables: GridTable<any>[] = [];
private tableDisposable: IDisposable[] = [];
private queryRunnerDisposables: IDisposable[] = [];
private runner: QueryRunner;
private maximizedGrid: GridTable<any>;
constructor(
title: string, options: IViewletPanelOptions,
@IKeybindingService keybindingService: IKeybindingService,
@IContextMenuService contextMenuService: IContextMenuService,
@IConfigurationService configurationService: IConfigurationService,
@IThemeService private themeService: IThemeService
) {
super(title, options, keybindingService, contextMenuService, configurationService);
this.splitView = new ScrollableSplitView(this.container, { enableResizing: false });
}
protected renderBody(container: HTMLElement): void {
this.container.style.width = '100%';
this.container.style.height = '100%';
container.appendChild(this.container);
}
protected layoutBody(size: number): void {
this.splitView.layout(size);
}
public set queryRunner(runner: QueryRunner) {
dispose(this.queryRunnerDisposables);
this.reset();
this.queryRunnerDisposables = [];
this.runner = runner;
this.queryRunnerDisposables.push(this.runner.onResultSet(e => this.onResultSet(e)));
this.queryRunnerDisposables.push(this.runner.onQueryStart(() => this.reset()));
}
private onResultSet(resultSet: sqlops.ResultSetSummary | sqlops.ResultSetSummary[]) {
this.addResultSet(resultSet);
this.tables.map(t => {
t.state.canBeMaximized = this.tables.length > 1;
});
this.maximumBodySize = this.tables.reduce((p, c) => {
return p + c.maximumSize;
}, 0);
}
private addResultSet(resultSet: sqlops.ResultSetSummary | sqlops.ResultSetSummary[]) {
let resultsToAdd: sqlops.ResultSetSummary[];
if (!Array.isArray(resultSet)) {
resultsToAdd = [resultSet];
} else {
resultsToAdd = resultSet;
}
let tables: GridTable<any>[] = [];
for (let set of resultsToAdd) {
let tableState = new GridTableState();
let table = new GridTable(this.runner, tableState, set, this.contextMenuService);
tableState.onMaximizedChange(e => {
if (e) {
this.maximizeTable(table.id);
} else {
this.minimizeTables();
}
});
this.tableDisposable.push(attachTableStyler(table, this.themeService));
tables.push(table);
}
if (isUndefinedOrNull(this.maximizedGrid)) {
this.splitView.addViews(tables, tables.map(i => i.minimumSize), this.splitView.length);
}
this.tables = this.tables.concat(tables);
}
private reset() {
for (let i = this.splitView.length - 1; i >= 0; i--) {
this.splitView.removeView(i);
}
dispose(this.tables);
this.tables = [];
}
private maximizeTable(tableid: string): void {
if (!this.tables.find(t => t.id === tableid)) {
return;
}
for (let i = this.tables.length - 1; i >= 0; i--) {
if (this.tables[i].id === tableid) {
this.tables[i].state.maximized = true;
this.maximizedGrid = this.tables[i];
continue;
}
this.splitView.removeView(i);
}
}
private minimizeTables(): void {
if (this.maximizedGrid) {
this.maximizedGrid.state.maximized = false;
this.maximizedGrid = undefined;
this.splitView.removeView(0);
this.splitView.addViews(this.tables, this.tables.map(i => i.minimumSize));
}
}
}
class GridTable<T> extends Disposable implements IView {
private static BOTTOMPADDING = 5;
private static ACTIONBAR_WIDTH = 26;
private table: Table<T>;
private actionBar: ActionBar;
private container = document.createElement('div');
private selectionModel = new CellSelectionModel();
private styles: ITableStyles;
private columns: Slick.Column<T>[];
private _onDidChange = new Emitter<number>();
public readonly onDidChange: Event<number> = this._onDidChange.event;
public id = generateUuid();
constructor(
private runner: QueryRunner,
public state: GridTableState,
private resultSet: sqlops.ResultSetSummary,
private contextMenuService: IContextMenuService
) {
super();
this.container.style.width = '100%';
this.container.style.height = '100%';
this.container.style.marginBottom = GridTable.BOTTOMPADDING + 'px';
this.container.className = 'grid-panel';
this.columns = this.resultSet.columnInfo.map((c, i) => {
return <Slick.Column<T>>{
id: i.toString(),
name: c.columnName,
field: i.toString(),
width: 100
};
});
}
public render(container: HTMLElement, orientation: Orientation): void {
container.appendChild(this.container);
}
private build(): void {
let tableContainer = document.createElement('div');
tableContainer.style.display = 'inline-block';
this.container.appendChild(tableContainer);
let collection = new VirtualizedCollection(50, this.resultSet.rowCount,
(offset, count) => this.loadData(offset, count),
index => this.placeholdGenerator(index)
);
collection.setCollectionChangedCallback((change, startIndex, count) => {
this.renderGridDataRowsRange(startIndex, count);
});
let numberColumn = new RowNumberColumn({ numberOfRows: this.resultSet.rowCount });
this.columns.unshift(numberColumn.getColumnDefinition());
this.table = this._register(new Table(tableContainer, { dataProvider: new AsyncDataProvider(collection), columns: this.columns }, { rowHeight, showRowNumber: true }));
this.table.setSelectionModel(this.selectionModel);
this.table.registerPlugin(new MouseWheelSupport());
this.table.registerPlugin(new AutoColumnSize());
this.table.registerPlugin(numberColumn);
this._register(this.table.onContextMenu(this.contextMenu, this));
if (this.styles) {
this.table.style(this.styles);
}
let actions = [];
if (this.state.canBeMaximized) {
if (this.state.maximized) {
actions.splice(1, 0, new MinimizeTableAction());
} else {
actions.splice(1, 0, new MaximizeTableAction());
}
}
actions.push(
new SaveResultAction(SaveResultAction.SAVECSV_ID, SaveResultAction.SAVECSV_LABEL, SaveResultAction.SAVECSV_ICON, SaveFormat.CSV),
new SaveResultAction(SaveResultAction.SAVEEXCEL_ID, SaveResultAction.SAVEEXCEL_LABEL, SaveResultAction.SAVEEXCEL_ICON, SaveFormat.EXCEL),
new SaveResultAction(SaveResultAction.SAVEJSON_ID, SaveResultAction.SAVEJSON_LABEL, SaveResultAction.SAVEJSON_ICON, SaveFormat.JSON)
);
let actionBarContainer = document.createElement('div');
actionBarContainer.style.width = GridTable.ACTIONBAR_WIDTH + 'px';
actionBarContainer.style.display = 'inline-block';
actionBarContainer.style.height = '100%';
actionBarContainer.style.verticalAlign = 'top';
this.container.appendChild(actionBarContainer);
this.actionBar = new ActionBar(actionBarContainer, {
orientation: ActionsOrientation.VERTICAL, context: {
runner: this.runner,
batchId: this.resultSet.batchId,
resultId: this.resultSet.id,
table: this.table,
tableState: this.state
}
});
this.actionBar.push(actions, { icon: true, label: false });
}
public layout(size: number): void {
if (!this.table) {
this.build();
}
this.table.layout(
new Dimension(
getContentWidth(this.container) - GridTable.ACTIONBAR_WIDTH,
size - GridTable.BOTTOMPADDING
)
);
}
public get minimumSize(): number {
let smallestRows = ((this.resultSet.rowCount) * rowHeight) + columnHeight + estimatedScrollBarHeight + GridTable.BOTTOMPADDING;
let smallestSize = (minGridHeightInRows * rowHeight) + columnHeight + estimatedScrollBarHeight + GridTable.BOTTOMPADDING;
return Math.min(smallestRows, smallestSize);
}
public get maximumSize(): number {
return ((this.resultSet.rowCount) * rowHeight) + columnHeight + estimatedScrollBarHeight + GridTable.BOTTOMPADDING;
}
private loadData(offset: number, count: number): Thenable<T[]> {
return this.runner.getQueryRows(offset, count, this.resultSet.batchId, this.resultSet.id).then(response => {
return response.resultSubset.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] = r[i - 1].displayValue;
}
return dataWithSchema as T;
});
});
}
private contextMenu(e: ITableContextMenuEvent): void {
const selection = this.selectionModel.getSelectedRanges();
const { cell } = e;
this.contextMenuService.showContextMenu({
getAnchor: () => e.anchor,
getActions: () => {
let actions = [
new SelectAllGridAction(),
new Separator(),
new SaveResultAction(SaveResultAction.SAVECSV_ID, SaveResultAction.SAVECSV_LABEL, SaveResultAction.SAVECSV_ICON, SaveFormat.CSV),
new SaveResultAction(SaveResultAction.SAVEEXCEL_ID, SaveResultAction.SAVEEXCEL_LABEL, SaveResultAction.SAVEEXCEL_ICON, SaveFormat.EXCEL),
new SaveResultAction(SaveResultAction.SAVEJSON_ID, SaveResultAction.SAVEJSON_LABEL, SaveResultAction.SAVEJSON_ICON, SaveFormat.JSON),
new Separator(),
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 MinimizeTableAction());
} else {
actions.splice(1, 0, new MaximizeTableAction());
}
}
return TPromise.as(actions);
},
getActionsContext: () => {
return <IGridActionContext>{
cell,
selection,
runner: this.runner,
batchId: this.resultSet.batchId,
resultId: this.resultSet.id,
table: this.table,
tableState: this.state
};
}
});
}
private placeholdGenerator(index: number): any {
return { values: [] };
}
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).destroy();
super.dispose();
}
}

View File

@@ -0,0 +1,32 @@
/* Disable repl hover highlight in tree. */
.monaco-workbench .message-tree .monaco-tree .monaco-tree-rows > .monaco-tree-row:hover:not(.highlighted):not(.selected):not(.focused) {
background-color: inherit;
}
/* Disable repl hover highlight in tree. */
.monaco-workbench .message-tree .monaco-tree .monaco-tree-row > .content {
line-height: 18px;
user-select: text;
word-wrap: break-word;
/* white-space: pre-wrap; */
word-break: break-all;
}
.message-tree .time-stamp {
width: 100px;
display: inline-block;
}
.message-tree .message,
.message-tree .batch-start {
display: inline-block;
}
.message-tree .batch-start {
text-decoration: underline;
}
.message-tree .batch-start:hover {
color: red;
}

View File

@@ -0,0 +1,343 @@
/*---------------------------------------------------------------------------------------------
* 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 'vs/css!./media/messagePanel';
import { IMessagesActionContext, SelectAllMessagesAction, CopyMessagesAction } from './actions';
import QueryRunner from 'sql/parts/query/execution/queryRunner';
import { IResultMessage, BatchSummary, ISelectionData } from 'sqlops';
import { ViewletPanel, IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet';
import { IDataSource, ITree, IRenderer, ContextMenuEvent } from 'vs/base/parts/tree/browser/tree';
import { TPromise } from 'vs/base/common/winjs.base';
import { generateUuid } from 'vs/base/common/uuid';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
import { attachListStyler } from 'vs/platform/theme/common/styler';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { OpenMode, ClickBehavior, ICancelableEvent, IControllerOptions } from 'vs/base/parts/tree/browser/treeDefaults';
import { WorkbenchTreeController } from 'vs/platform/list/browser/listService';
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
import { $ } from 'vs/base/browser/builder';
import { isArray } from 'vs/base/common/types';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { localize } from 'vs/nls';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditor } from 'vs/editor/common/editorCommon';
export interface IResultMessageIntern extends IResultMessage {
id?: string;
}
export interface IMessagePanelMessage {
message: string;
isError: boolean;
}
export interface IMessagePanelBatchMessage extends IMessagePanelMessage {
selection: ISelectionData;
time: string;
}
interface IMessageTemplate {
message: HTMLElement;
}
interface IBatchTemplate extends IMessageTemplate {
timeStamp: HTMLElement;
}
const TemplateIds = {
MESSAGE: 'message',
BATCH: 'batch',
MODEL: 'model'
};
export class MessagePanel extends ViewletPanel {
private ds = new MessageDataSource();
private renderer = new MessageRenderer();
private model = new Model();
private controller: MessageController;
private container = $('div message-tree').getHTMLElement();
private queryRunnerDisposables: IDisposable[] = [];
private tree: ITree;
constructor(
title: string, options: IViewletPanelOptions,
@IKeybindingService keybindingService: IKeybindingService,
@IContextMenuService contextMenuService: IContextMenuService,
@IConfigurationService configurationService: IConfigurationService,
@IThemeService private themeService: IThemeService,
@IInstantiationService instantiationService: IInstantiationService
) {
super(title, options, keybindingService, contextMenuService, configurationService);
this.controller = instantiationService.createInstance(MessageController, { openMode: OpenMode.SINGLE_CLICK, clickBehavior: ClickBehavior.ON_MOUSE_UP /* do not change, to preserve focus behaviour in input field */ });
this.controller.toFocusOnClick = this.model;
this.tree = new Tree(this.container, {
dataSource: this.ds,
renderer: this.renderer,
controller: this.controller
}, { keyboardSupport: false });
}
protected renderBody(container: HTMLElement): void {
this.container.style.width = '100%';
this.container.style.height = '100%';
attachListStyler(this.tree, this.themeService);
container.appendChild(this.container);
this.tree.setInput(this.model);
}
protected layoutBody(size: number): void {
const previousScrollPosition = this.tree.getScrollPosition();
this.tree.layout(size);
if (previousScrollPosition === 1) {
this.tree.setScrollPosition(1);
}
}
public set queryRunner(runner: QueryRunner) {
dispose(this.queryRunnerDisposables);
this.queryRunnerDisposables = [];
this.reset();
this.queryRunnerDisposables.push(runner.onQueryStart(() => this.reset()));
this.queryRunnerDisposables.push(runner.onBatchStart(e => this.onBatchStart(e)));
this.queryRunnerDisposables.push(runner.onMessage(e => this.onMessage(e)));
this.queryRunnerDisposables.push(runner.onQueryEnd(e => this.onQueryEnd(e)));
}
private onMessage(message: IResultMessage | IResultMessage[]) {
if (isArray(message)) {
this.model.messages.push(...message.map(c => {
return <IMessagePanelMessage>{
isError: c.isError,
message: c.message
};
}));
} else {
this.model.messages.push({
message: message.message,
isError: message.isError
});
}
const previousScrollPosition = this.tree.getScrollPosition();
this.tree.refresh(this.model).then(() => {
if (previousScrollPosition === 1) {
this.tree.setScrollPosition(1);
}
});
}
private onBatchStart(batch: BatchSummary) {
this.model.messages.push({
message: localize('query.message.startQuery', 'Started executing query at Line {0}', batch.selection.startLine),
time: new Date(batch.executionStart).toLocaleTimeString(),
selection: batch.selection,
isError: false
});
const previousScrollPosition = this.tree.getScrollPosition();
this.tree.refresh(this.model).then(() => {
if (previousScrollPosition === 1) {
this.tree.setScrollPosition(1);
}
});
}
private onQueryEnd(elapsedTime: string) {
this.model.totalExecuteMessage = {
message: localize('query.message.executionTime', 'Total execution time: {0}', elapsedTime),
isError: false
};
const previousScrollPosition = this.tree.getScrollPosition();
this.tree.refresh(this.model).then(() => {
if (previousScrollPosition === 1) {
this.tree.setScrollPosition(1);
}
});
}
private reset() {
this.model.messages = [];
this.model.totalExecuteMessage = undefined;
this.tree.refresh(this.model);
}
}
class MessageDataSource implements IDataSource {
getId(tree: ITree, element: Model | IResultMessageIntern): string {
if (element instanceof Model) {
return element.uuid;
} else {
if (!element.id) {
element.id = generateUuid();
}
return element.id;
}
}
hasChildren(tree: ITree, element: any): boolean {
return element instanceof Model;
}
getChildren(tree: ITree, element: any): TPromise {
if (element instanceof Model) {
let messages = element.messages;
if (element.totalExecuteMessage) {
messages = messages.concat(element.totalExecuteMessage);
}
return TPromise.as(messages);
} else {
return TPromise.as(undefined);
}
}
getParent(tree: ITree, element: any): TPromise {
return TPromise.as(null);
}
}
class MessageRenderer implements IRenderer {
getHeight(tree: ITree, element: any): number {
return 22;
}
getTemplateId(tree: ITree, element: any): string {
if (element instanceof Model) {
return TemplateIds.MODEL;
} else if (element.selection) {
return TemplateIds.BATCH;
} else {
return TemplateIds.MESSAGE;
}
}
renderTemplate(tree: ITree, templateId: string, container: HTMLElement): IMessageTemplate | IBatchTemplate {
if (templateId === TemplateIds.MESSAGE) {
$('div.time-stamp').appendTo(container);
const message = $('div.message').appendTo(container).getHTMLElement();
return { message };
} else if (templateId === TemplateIds.BATCH) {
const timeStamp = $('div.time-stamp').appendTo(container).getHTMLElement();
const message = $('div.batch-start').appendTo(container).getHTMLElement();
return { message, timeStamp };
} else {
return undefined;
}
}
renderElement(tree: ITree, element: IResultMessage, templateId: string, templateData: IMessageTemplate | IBatchTemplate): void {
if (templateId === TemplateIds.MESSAGE) {
let data: IMessageTemplate = templateData;
data.message.innerText = element.message;
} else if (templateId === TemplateIds.BATCH) {
let data = templateData as IBatchTemplate;
data.timeStamp.innerText = element.time;
data.message.innerText = element.message;
}
}
disposeTemplate(tree: ITree, templateId: string, templateData: any): void {
}
}
export class MessageController extends WorkbenchTreeController {
private lastSelectedString: string = null;
public toFocusOnClick: { focus(): void };
constructor(
options: IControllerOptions,
@IConfigurationService configurationService: IConfigurationService,
@IWorkbenchEditorService private workbenchEditorService: IWorkbenchEditorService,
@IContextMenuService private contextMenuService: IContextMenuService,
@IInstantiationService private instantiationService: IInstantiationService
) {
super(options, configurationService);
}
protected onLeftClick(tree: ITree, element: any, eventish: ICancelableEvent, origin: string = 'mouse'): boolean {
const mouseEvent = <IMouseEvent>eventish;
// input and output are one element in the tree => we only expand if the user clicked on the output.
// if ((element.reference > 0 || (element instanceof RawObjectReplElement && element.hasChildren)) && mouseEvent.target.className.indexOf('input expression') === -1) {
super.onLeftClick(tree, element, eventish, origin);
tree.clearFocus();
tree.deselect(element);
// }
const selection = window.getSelection();
if (selection.type !== 'Range' || this.lastSelectedString === selection.toString()) {
// only focus the input if the user is not currently selecting.
this.toFocusOnClick.focus();
}
this.lastSelectedString = selection.toString();
if (element.selection) {
let selection: ISelectionData = element.selection;
// this is a batch statement
let control = this.workbenchEditorService.getActiveEditor().getControl() as IEditor;
control.setSelection({
startColumn: selection.startColumn + 1,
endColumn: selection.endColumn + 1,
endLineNumber: selection.endLine + 1,
startLineNumber: selection.startLine + 1
});
control.focus();
}
return true;
}
public onContextMenu(tree: ITree, element: any, event: ContextMenuEvent): boolean {
if (event.target && event.target.tagName && event.target.tagName.toLowerCase() === 'input') {
return false; // allow context menu on input fields
}
// Prevent native context menu from showing up
if (event) {
event.preventDefault();
event.stopPropagation();
}
const selection = document.getSelection();
this.contextMenuService.showContextMenu({
getAnchor: () => {
return { x: event.posx, y: event.posy };
},
getActions: () => {
return TPromise.as([
this.instantiationService.createInstance(CopyMessagesAction),
new SelectAllMessagesAction()
]);
},
getActionsContext: () => {
return <IMessagesActionContext>{
selection,
tree
};
}
});
return true;
}
}
export class Model {
public messages: Array<IMessagePanelMessage | IMessagePanelBatchMessage> = [];
public totalExecuteMessage: IMessagePanelMessage;
public uuid = generateUuid();
public focus() {
}
}

View File

@@ -6,7 +6,6 @@
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import { Builder } from 'vs/base/browser/builder';
import { EditorOptions } from 'vs/workbench/common/editor';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
@@ -14,10 +13,10 @@ import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { BareFontInfo } from 'vs/editor/common/config/fontInfo';
import { getZoomLevel } from 'vs/base/browser/browser';
import { Configuration } from 'vs/editor/browser/config/configuration';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import * as DOM from 'vs/base/browser/dom';
import * as types from 'vs/base/common/types';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { QueryResultsInput } from 'sql/parts/query/common/queryResultsInput';
import { IQueryModelService } from 'sql/parts/query/execution/queryModel';
@@ -25,8 +24,7 @@ import { bootstrapAngular } from 'sql/services/bootstrap/bootstrapService';
import { IQueryComponentParams } from 'sql/services/bootstrap/bootstrapParams';
import { QueryOutputModule } from 'sql/parts/query/views/queryOutput.module';
import { QUERY_OUTPUT_SELECTOR } from 'sql/parts/query/views/queryOutput.component';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { Event } from 'vs/base/common/event';
import { QueryResultsView } from 'sql/parts/query/editor/queryResultsView';
export const RESULTS_GRID_DEFAULTS = {
cellPadding: [6, 10, 5],
@@ -97,6 +95,8 @@ export class QueryResultsEditor extends BaseEditor {
protected _rawOptions: BareResultsGridInfo;
protected _input: QueryResultsInput;
private resultsView: QueryResultsView;
constructor(
@ITelemetryService telemetryService: ITelemetryService,
@IThemeService themeService: IThemeService,
@@ -106,12 +106,12 @@ export class QueryResultsEditor extends BaseEditor {
) {
super(QueryResultsEditor.ID, telemetryService, themeService);
this._rawOptions = BareResultsGridInfo.createFromRawSettings(this._configurationService.getValue('resultsGrid'), getZoomLevel());
this._configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration('resultsGrid')) {
this._rawOptions = BareResultsGridInfo.createFromRawSettings(this._configurationService.getValue('resultsGrid'), getZoomLevel());
this.applySettings();
}
});
// this._configurationService.onDidChangeConfiguration(e => {
// if (e.affectsConfiguration('resultsGrid')) {
// this._rawOptions = BareResultsGridInfo.createFromRawSettings(this._configurationService.getValue('resultsGrid'), getZoomLevel());
// this.applySettings();
// }
// });
}
public get input(): QueryResultsInput {
@@ -136,54 +136,22 @@ export class QueryResultsEditor extends BaseEditor {
}
createEditor(parent: HTMLElement): void {
if (!this.resultsView) {
this.resultsView = new QueryResultsView(parent, this._instantiationService, this._queryModelService);
}
}
layout(dimension: DOM.Dimension): void {
this.resultsView.layout(dimension);
}
setInput(input: QueryResultsInput, options: EditorOptions): TPromise<void> {
super.setInput(input, options);
this.applySettings();
if (!input.hasBootstrapped) {
this._bootstrapAngular();
}
this.resultsView.input = input;
return TPromise.wrap<void>(null);
}
/**
* Load the angular components and record for this input that we have done so
*/
private _bootstrapAngular(): void {
let input = <QueryResultsInput>this.input;
let uri = input.uri;
// Pass the correct DataService to the new angular component
let dataService = this._queryModelService.getDataService(uri);
if (!dataService) {
throw new Error('DataService not found for URI: ' + uri);
}
// Mark that we have bootstrapped
input.setBootstrappedTrue();
// Get the bootstrap params and perform the bootstrap
// Note: pass in input so on disposal this is cleaned up.
// Otherwise many components will be left around and be subscribed
// to events from the backing data service
let params: IQueryComponentParams = {
dataService: dataService,
onSaveViewState: this.input.onSaveViewStateEmitter.event,
onRestoreViewState: this.input.onRestoreViewStateEmitter.event
};
bootstrapAngular(this._instantiationService,
QueryOutputModule,
this.getContainer(),
QUERY_OUTPUT_SELECTOR,
params,
input);
}
public dispose(): void {
super.dispose();
}
}
}

View File

@@ -0,0 +1,122 @@
/*---------------------------------------------------------------------------------------------
* 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 { QueryResultsInput } from 'sql/parts/query/common/queryResultsInput';
import { TabbedPanel, IPanelTab, IPanelView } from 'sql/base/browser/ui/panel/panel';
import { IQueryModelService } from '../execution/queryModel';
import QueryRunner from 'sql/parts/query/execution/queryRunner';
import { MessagePanel } from './messagePanel';
import { GridPanel } from './gridPanel';
import * as nls from 'vs/nls';
import * as UUID from 'vs/base/common/uuid';
import { PanelViewlet } from 'vs/workbench/browser/parts/views/panelViewlet';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import * as DOM from 'vs/base/browser/dom';
import { Emitter } from 'vs/base/common/event';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
class ResultsView implements IPanelView {
private panelViewlet: PanelViewlet;
private gridPanel: GridPanel;
private messagePanel: MessagePanel;
private container = document.createElement('div');
private _onRemove = new Emitter<void>();
public readonly onRemove = this._onRemove.event;
private _onLayout = new Emitter<void>();
public readonly onLayout = this._onLayout.event;
private queryRunnerDisposable: IDisposable[] = [];
constructor(instantiationService: IInstantiationService) {
this.panelViewlet = instantiationService.createInstance(PanelViewlet, 'resultsView', { showHeaderInTitleWhenSingleView: false });
this.gridPanel = instantiationService.createInstance(GridPanel, nls.localize('gridPanel', 'Results'), {});
this.messagePanel = instantiationService.createInstance(MessagePanel, nls.localize('messagePanel', 'Messages'), {});
this.panelViewlet.create(this.container).then(() => {
this.panelViewlet.addPanels([
{ panel: this.gridPanel, size: 1000, index: 0 },
{ panel: this.messagePanel, size: this.messagePanel.minimumSize, index: 1 }
]);
});
}
render(container: HTMLElement): void {
container.appendChild(this.container);
}
layout(dimension: DOM.Dimension): void {
this.panelViewlet.layout(dimension);
}
remove(): void {
this.container.remove();
}
public set queryRunner(runner: QueryRunner) {
this.gridPanel.queryRunner = runner;
this.messagePanel.queryRunner = runner;
}
}
class ResultsTab implements IPanelTab {
public readonly title = nls.localize('resultsTabTitle', 'Results');
public readonly identifier = UUID.generateUuid();
public readonly view: ResultsView;
private _isAttached = false;
constructor(instantiationService: IInstantiationService) {
this.view = new ResultsView(instantiationService);
this.view.onLayout(() => this._isAttached = true, this);
this.view.onRemove(() => this._isAttached = false, this);
}
public isAttached(): boolean {
return this._isAttached;
}
public set queryRunner(runner: QueryRunner) {
this.view.queryRunner = runner;
}
}
export class QueryResultsView {
private _panelView: TabbedPanel;
private _input: QueryResultsInput;
private resultsTab: ResultsTab;
constructor(
container: HTMLElement,
@IInstantiationService instantiationService: IInstantiationService,
@IQueryModelService private queryModelService: IQueryModelService
) {
this.resultsTab = new ResultsTab(instantiationService);
this._panelView = new TabbedPanel(container, { showHeaderWhenSingleView: false });
}
public style() {
}
public set input(input: QueryResultsInput) {
this._input = input;
this.resultsTab.queryRunner = this.queryModelService._getQueryInfo(input.uri).queryRunner;
// if (!this.resultsTab.isAttached) {
this._panelView.pushTab(this.resultsTab);
// }
}
public get input(): QueryResultsInput {
return this._input;
}
public layout(dimension: DOM.Dimension) {
this._panelView.layout(dimension);
}
}

View File

@@ -449,7 +449,7 @@ export class ListDatabasesActionItem extends EventEmitter implements IActionItem
strictSelection: true,
placeholder: selectString,
ariaLabel: selectString,
actionLabel: nls.localize('toggleDatabaseNameDropdown', 'Select Database Toggle Dropdown')
actionLabel: nls.localize('listDatabases.toggleDatabaseNameDropdown', 'Select Database Toggle Dropdown')
});
this._dropdown.onValueChange(s => this.databaseSelected(s));

View File

@@ -5,7 +5,6 @@
import QueryRunner from 'sql/parts/query/execution/queryRunner';
import { DataService } from 'sql/parts/grid/services/dataService';
import { ISlickRange } from 'angular2-slickgrid';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { Event } from 'vs/base/common/event';
import { QueryInput } from 'sql/parts/query/common/queryInput';
@@ -19,6 +18,7 @@ import {
EditRevertCellResult,
ExecutionPlanOptions
} from 'sqlops';
import { QueryInfo } from 'sql/parts/query/execution/queryModelService';
export const SERVICE_ID = 'queryModelService';
@@ -48,7 +48,7 @@ export interface IQueryModelService {
resizeResultsets(uri: string): void;
onAngularLoaded(uri: string): void;
copyResults(uri: string, selection: ISlickRange[], batchId: number, resultId: number, includeHeaders?: boolean): void;
copyResults(uri: string, selection: Slick.Range[], batchId: number, resultId: number, includeHeaders?: boolean): void;
setEditorSelection(uri: string, index: number): void;
showWarning(uri: string, message: string): void;
showError(uri: string, message: string): void;
@@ -69,6 +69,7 @@ export interface IQueryModelService {
revertRow(ownerUri: string, rowId: number): Thenable<void>;
getEditRows(ownerUri: string, rowStart: number, numberOfRows: number): Thenable<EditSubsetResult>;
_getQueryInfo(uri: string): QueryInfo;
// Edit Data Callbacks
onEditSessionReady: Event<EditSessionReadyParams>;
}

View File

@@ -16,7 +16,6 @@ import { SqlFlavorStatusbarItem } from 'sql/parts/query/common/flavorStatus';
import { RowCountStatusBarItem } from 'sql/parts/query/common/rowCountStatus';
import * as sqlops from 'sqlops';
import { ISlickRange } from 'angular2-slickgrid';
import * as nls from 'vs/nls';
import * as statusbar from 'vs/workbench/browser/parts/statusbar/statusbar';
@@ -31,7 +30,7 @@ import Severity from 'vs/base/common/severity';
const selectionSnippetMaxLen = 100;
interface QueryEvent {
export interface QueryEvent {
type: string;
data: any;
}
@@ -39,7 +38,7 @@ interface QueryEvent {
/**
* Holds information about the state of a query runner
*/
class QueryInfo {
export class QueryInfo {
public queryRunner: QueryRunner;
public dataService: DataService;
public queryEventQueue: QueryEvent[];
@@ -170,7 +169,7 @@ export class QueryModelService implements IQueryModelService {
return undefined;
}
public copyResults(uri: string, selection: ISlickRange[], batchId: number, resultId: number, includeHeaders?: boolean): void {
public copyResults(uri: string, selection: Slick.Range[], batchId: number, resultId: number, includeHeaders?: boolean): void {
this._queryInfoMap.get(uri).queryRunner.copyResults(selection, batchId, resultId, includeHeaders);
}
@@ -569,7 +568,7 @@ export class QueryModelService implements IQueryModelService {
}
}
private _getQueryInfo(uri: string): QueryInfo {
public _getQueryInfo(uri: string): QueryInfo {
return this._queryInfoMap.get(uri);
}

View File

@@ -10,8 +10,8 @@ import * as sqlops from 'sqlops';
import * as Constants from 'sql/parts/query/common/constants';
import * as WorkbenchUtils from 'sql/workbench/common/sqlWorkbenchUtils';
import { IQueryManagementService } from 'sql/parts/query/common/queryManagement';
import { ISlickRange } from 'angular2-slickgrid';
import * as Utils from 'sql/parts/connection/common/utils';
import { SaveFormat } from 'sql/parts/grid/common/interfaces';
import Severity from 'vs/base/common/severity';
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
@@ -21,6 +21,9 @@ import * as types from 'vs/base/common/types';
import { EventEmitter } from 'sql/base/common/eventEmitter';
import { IDisposable } from 'vs/base/common/lifecycle';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { Emitter, echo, debounceEvent, Event } from 'vs/base/common/event';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ResultSerializer } from 'sql/parts/query/common/resultSerializer';
export interface IEditSessionReadyEvent {
ownerUri: string;
@@ -61,6 +64,38 @@ export default class QueryRunner {
private _batchSets: sqlops.BatchSummary[] = [];
private _eventEmitter = new EventEmitter();
private _onMessage = new Emitter<sqlops.IResultMessage>();
public readonly onMessage = debounceEvent<sqlops.IResultMessage, sqlops.IResultMessage[]>(echo(this._onMessage.event), (l, e) => {
// on first run
if (types.isUndefinedOrNull(l)) {
return [e];
} else {
return l.concat(e);
}
});
private _onResultSet = new Emitter<sqlops.ResultSetSummary>();
public readonly onResultSet = debounceEvent<sqlops.ResultSetSummary, sqlops.ResultSetSummary[]>(echo(this._onResultSet.event), (l, e) => {
// on first run
if (types.isUndefinedOrNull(l)) {
return [e];
} else {
return l.concat(e);
}
});
private _onQueryStart = new Emitter<void>();
public readonly onQueryStart: Event<void> = echo(this._onQueryStart.event);
private _onQueryEnd = new Emitter<string>();
public readonly onQueryEnd: Event<string> = echo(this._onQueryEnd.event);
private _onBatchStart = new Emitter<sqlops.BatchSummary>();
public readonly onBatchStart: Event<sqlops.BatchSummary> = echo(this._onBatchStart.event);
private _onBatchEnd = new Emitter<sqlops.BatchSummary>();
public readonly onBatchEnd: Event<sqlops.BatchSummary> = echo(this._onBatchEnd.event);
// CONSTRUCTOR /////////////////////////////////////////////////////////
constructor(
public uri: string,
@@ -68,7 +103,8 @@ export default class QueryRunner {
@IQueryManagementService private _queryManagementService: IQueryManagementService,
@INotificationService private _notificationService: INotificationService,
@IWorkspaceConfigurationService private _workspaceConfigurationService: IWorkspaceConfigurationService,
@IClipboardService private _clipboardService: IClipboardService
@IClipboardService private _clipboardService: IClipboardService,
@IInstantiationService private instantiationService: IInstantiationService
) { }
get isExecuting(): boolean {
@@ -152,6 +188,7 @@ export default class QueryRunner {
private handleSuccessRunQueryResult() {
// The query has started, so lets fire up the result pane
this._onQueryStart.fire();
this._eventEmitter.emit(EventType.START);
this._queryManagementService.registerRunner(this, this.uri);
}
@@ -187,8 +224,9 @@ export default class QueryRunner {
}
});
// We're done with this query so shut down any waiting mechanisms
this._eventEmitter.emit(EventType.COMPLETE, Utils.parseNumAsTimeString(this._totalElapsedMilliseconds));
// We're done with this query so shut down any waiting mechanisms
this._onQueryEnd.fire(Utils.parseNumAsTimeString(this._totalElapsedMilliseconds));
}
/**
@@ -209,6 +247,7 @@ export default class QueryRunner {
// Store the batch
this.batchSets[batch.id] = batch;
this._eventEmitter.emit(EventType.BATCH_START, batch);
this._onBatchStart.fire(batch);
}
/**
@@ -225,7 +264,9 @@ export default class QueryRunner {
// send a time message in the format used for query complete
this.sendBatchTimeMessage(batch.id, Utils.parseNumAsTimeString(executionTime));
}
this._eventEmitter.emit(EventType.BATCH_COMPLETE, batch);
this._onBatchEnd.fire(batch);
}
/**
@@ -256,6 +297,7 @@ export default class QueryRunner {
// Store the result set in the batch and emit that a result set has completed
batchSet.resultSetSummaries[resultSet.id] = resultSet;
this._eventEmitter.emit(EventType.RESULT_SET, resultSet);
this._onResultSet.fire(resultSet);
}
}
}
@@ -269,13 +311,13 @@ export default class QueryRunner {
// Send the message to the results pane
this._eventEmitter.emit(EventType.MESSAGE, message);
this._onMessage.fire(message);
}
/**
* Get more data rows from the current resultSets from the service layer
*/
public getQueryRows(rowStart: number, numberOfRows: number, batchIndex: number, resultSetIndex: number): Thenable<sqlops.QueryExecuteSubsetResult> {
const self = this;
let rowData: sqlops.QueryExecuteSubsetParams = <sqlops.QueryExecuteSubsetParams>{
ownerUri: this.uri,
resultSetIndex: resultSetIndex,
@@ -284,16 +326,12 @@ export default class QueryRunner {
batchIndex: batchIndex
};
return new Promise<sqlops.QueryExecuteSubsetResult>((resolve, reject) => {
self._queryManagementService.getQueryRows(rowData).then(result => {
resolve(result);
}, error => {
self._notificationService.notify({
severity: Severity.Error,
message: nls.localize('query.gettingRowsFailedError', 'Something went wrong getting more rows: {0}', error)
});
reject(error);
return this._queryManagementService.getQueryRows(rowData).then(r => r, error => {
this._notificationService.notify({
severity: Severity.Error,
message: nls.localize('query.gettingRowsFailedError', 'Something went wrong getting more rows: {0}', error)
});
return error;
});
}
@@ -317,7 +355,7 @@ export default class QueryRunner {
this._isExecuting = false;
this._notificationService.notify({
severity: Severity.Error,
message: nls.localize('query.initEditExecutionFailed', 'Init Edit Execution failed: ') + error
message: nls.localize('query.initEditExecutionFailed', 'Init Edit Execution failed: ') + error
});
});
}
@@ -341,7 +379,7 @@ export default class QueryRunner {
let error = `Nothing returned from subset query`;
self._notificationService.notify({
severity: Severity.Error,
message: error
message: error
});
reject(error);
}
@@ -350,7 +388,7 @@ export default class QueryRunner {
let errorMessage = nls.localize('query.moreRowsFailedError', 'Something went wrong getting more rows:');
self._notificationService.notify({
severity: Severity.Error,
message: `${errorMessage} ${error}`
message: `${errorMessage} ${error}`
});
reject(error);
});
@@ -412,7 +450,7 @@ export default class QueryRunner {
* @param resultId The result id of the result to copy from
* @param includeHeaders [Optional]: Should column headers be included in the copy selection
*/
copyResults(selection: ISlickRange[], batchId: number, resultId: number, includeHeaders?: boolean): void {
copyResults(selection: Slick.Range[], batchId: number, resultId: number, includeHeaders?: boolean): void {
const self = this;
let copyString = '';
const eol = this.getEolString();
@@ -421,6 +459,12 @@ export default class QueryRunner {
let tasks = selection.map((range, i) => {
return () => {
return self.getQueryRows(range.fromRow, range.toRow - range.fromRow + 1, batchId, resultId).then((result) => {
// If there was a previous selection separate it with a line break. Currently
// when there are multiple selections they are never on the same line
if (i > 0) {
copyString += eol;
}
if (self.shouldIncludeHeaders(includeHeaders)) {
let columnHeaders = self.getColumnHeaders(batchId, resultId, range);
if (columnHeaders !== undefined) {
@@ -477,7 +521,7 @@ export default class QueryRunner {
return !!removeNewLines;
}
private getColumnHeaders(batchId: number, resultId: number, range: ISlickRange): string[] {
private getColumnHeaders(batchId: number, resultId: number, range: Slick.Range): string[] {
let headers: string[] = undefined;
let batchSummary: sqlops.BatchSummary = this.batchSets[batchId];
if (batchSummary !== undefined) {
@@ -513,7 +557,11 @@ export default class QueryRunner {
isError: false
};
// Send the message to the results pane
this._eventEmitter.emit(EventType.MESSAGE, message);
this._onMessage.fire(message);
}
}
public serializeResults(batchId: number, resultSetId: number, format: SaveFormat, selection: Slick.Range[]) {
return this.instantiationService.createInstance(ResultSerializer).saveResults(this.uri, { selection, format, batchIndex: batchId, resultSetNumber: resultSetId });
}
}

View File

@@ -62,7 +62,7 @@ export class QueryEditorService implements IQueryEditorService {
@IWorkbenchEditorService private _editorService: IWorkbenchEditorService,
@IEditorGroupService private _editorGroupService: IEditorGroupService,
@INotificationService private _notificationService: INotificationService,
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService,
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService
) {
QueryEditorService.editorService = _editorService;
QueryEditorService.instantiationService = _instantiationService;

View File

@@ -103,7 +103,7 @@ export class TopOperationsComponent extends TabChild implements OnDestroy, OnIni
column.rerenderOnResize = true;
return column;
});
this._table = new Table(this._el.nativeElement, data, columns);
this._table = new Table(this._el.nativeElement, { dataProvider: data, columns });
this._disposables.push(attachTableStyler(this._table, this.themeService));
}
}

Some files were not shown because too many files have changed in this diff Show More