mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-19 18:46:52 -05:00
Compare commits
41 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9717b7516d | ||
|
|
b92c23df2b | ||
|
|
df617f19e0 | ||
|
|
fb565c2548 | ||
|
|
2bfc3a6c85 | ||
|
|
4ab5d84b94 | ||
|
|
154213b705 | ||
|
|
4cce29ea9d | ||
|
|
9d5d00aa8f | ||
|
|
70d47c1757 | ||
|
|
befa34790f | ||
|
|
84da9d289b | ||
|
|
7a30d535e8 | ||
|
|
199701d26b | ||
|
|
3ac2cfa528 | ||
|
|
78d68aa1b9 | ||
|
|
8042b78f1e | ||
|
|
000d064276 | ||
|
|
600a78f35f | ||
|
|
6f5e4c30dc | ||
|
|
8e7457911e | ||
|
|
43b3207937 | ||
|
|
22f2151c21 | ||
|
|
7c744f2307 | ||
|
|
08d57fed86 | ||
|
|
6e8a0fe0ef | ||
|
|
6668ec4b5d | ||
|
|
08a8288293 | ||
|
|
3001640eec | ||
|
|
efa3658ced | ||
|
|
92bc253cf7 | ||
|
|
a190190843 | ||
|
|
9c40bd1a23 | ||
|
|
dc2193138d | ||
|
|
033c8cb8b1 | ||
|
|
21c4429c6e | ||
|
|
348a96b033 | ||
|
|
0c930d7c0f | ||
|
|
98aca2b988 | ||
|
|
8d7f497e0c | ||
|
|
1b6328b451 |
2
.yarnrc
2
.yarnrc
@@ -1,3 +1,3 @@
|
||||
disturl "https://atom.io/download/electron"
|
||||
target "1.7.12"
|
||||
target "1.7.16"
|
||||
runtime "electron"
|
||||
|
||||
@@ -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'));
|
||||
});
|
||||
|
||||
|
||||
@@ -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}"
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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: ''
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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': ['*']
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
4
extensions/insights-default/yarn.lock
Normal file
4
extensions/insights-default/yarn.lock
Normal file
@@ -0,0 +1,4 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
4
extensions/markdown-basics/yarn.lock
Normal file
4
extensions/markdown-basics/yarn.lock
Normal file
@@ -0,0 +1,4 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
@@ -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": {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
2568
resources/xlf/sqlops-core/sql.xlf
Normal file
2568
resources/xlf/sqlops-core/sql.xlf
Normal file
File diff suppressed because it is too large
Load Diff
589
resources/xlf/vscode-extensions/agent.xlf
Normal file
589
resources/xlf/vscode-extensions/agent.xlf
Normal 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"><all databases></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>
|
||||
140
resources/xlf/vscode-extensions/import.xlf
Normal file
140
resources/xlf/vscode-extensions/import.xlf
Normal 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>
|
||||
29
resources/xlf/vscode-extensions/profiler.xlf
Normal file
29
resources/xlf/vscode-extensions/profiler.xlf
Normal 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>
|
||||
1553
samples/extensionSamples/package-lock.json
generated
1553
samples/extensionSamples/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>();
|
||||
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
212
src/sql/base/browser/ui/scrollableSplitview/heightMap.ts
Normal file
212
src/sql/base/browser/ui/scrollableSplitview/heightMap.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 = [];
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
215
src/sql/base/browser/ui/table/asyncDataView.ts
Normal file
215
src/sql/base/browser/ui/table/asyncDataView.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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."));
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.')
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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());
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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[];
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 });
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 }[];
|
||||
|
||||
@@ -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)
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}));
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
}));
|
||||
|
||||
|
||||
@@ -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);
|
||||
}));
|
||||
|
||||
|
||||
@@ -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>
|
||||
`
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 }));
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
|
||||
160
src/sql/parts/query/editor/actions.ts
Normal file
160
src/sql/parts/query/editor/actions.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
414
src/sql/parts/query/editor/gridPanel.ts
Normal file
414
src/sql/parts/query/editor/gridPanel.ts
Normal 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();
|
||||
}
|
||||
}
|
||||
32
src/sql/parts/query/editor/media/messagePanel.css
Normal file
32
src/sql/parts/query/editor/media/messagePanel.css
Normal 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;
|
||||
}
|
||||
343
src/sql/parts/query/editor/messagePanel.ts
Normal file
343
src/sql/parts/query/editor/messagePanel.ts
Normal 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() {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
122
src/sql/parts/query/editor/queryResultsView.ts
Normal file
122
src/sql/parts/query/editor/queryResultsView.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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>;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user