mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-22 09:35:37 -05:00
Another code layering (#4037)
* working on formatting * fixed basic lint errors; starting moving things to their appropriate location * formatting * update tslint to match the version of vscode we have * remove unused code * work in progress fixing layering * formatting * moved connection management service to platform * formatting * add missing file * moving more servies * formatting * moving more services * formatting * wip * moving more services * formatting * move css file * add missing svgs * moved the rest of services * formatting * changing around some references * formatting * revert tslint * revert some changes that brake things * formatting * fix tests * fix testzx * fix tests * fix tests * fix compile issue
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { ITree } from 'vs/base/parts/tree/browser/tree';
|
||||
import treedefaults = require('vs/base/parts/tree/browser/treeDefaults');
|
||||
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
|
||||
/**
|
||||
* Extends the tree controller to handle mouse and keyboard events on the tree elements
|
||||
*/
|
||||
export class FileBrowserController extends treedefaults.DefaultController {
|
||||
|
||||
constructor() {
|
||||
super({ clickBehavior: treedefaults.ClickBehavior.ON_MOUSE_DOWN, openMode: treedefaults.OpenMode.SINGLE_CLICK });
|
||||
}
|
||||
|
||||
protected onLeftClick(tree: ITree, element: any, event: IMouseEvent, origin: string = 'mouse'): boolean {
|
||||
// In file browser, double clicking an element calls tree.dispose(). There should not be any tree events after selection.
|
||||
if (event.detail === 2) {
|
||||
var payload = { origin: origin, originalEvent: event };
|
||||
if (tree.getInput() === element) {
|
||||
tree.clearFocus(payload);
|
||||
tree.clearSelection(payload);
|
||||
} else {
|
||||
var isMouseDown = event && event.browserEvent && event.browserEvent.type === 'mousedown';
|
||||
if (!isMouseDown) {
|
||||
event.preventDefault(); // we cannot preventDefault onMouseDown because this would break DND otherwise
|
||||
}
|
||||
event.stopPropagation();
|
||||
tree.domFocus();
|
||||
tree.setSelection([element], payload);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return super.onLeftClick(tree, element, event, origin);
|
||||
}
|
||||
}
|
||||
|
||||
protected onEnter(tree: ITree, event: IKeyboardEvent): boolean {
|
||||
var payload = { origin: 'keyboard', originalEvent: event };
|
||||
|
||||
if (tree.getHighlight()) {
|
||||
return false;
|
||||
}
|
||||
var focus = tree.getFocus();
|
||||
if (focus) {
|
||||
// In file browser, pressing enter key on an element will close dialog and call tree.dispose(). There should not be any tree events after selection.
|
||||
tree.setSelection([focus], payload);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { IFileBrowserService } from 'sql/platform/fileBrowser/common/interfaces';
|
||||
import { FileNode } from 'sql/workbench/services/fileBrowser/common/fileNode';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { ITree, IDataSource } from 'vs/base/parts/tree/browser/tree';
|
||||
|
||||
/**
|
||||
* Implements the DataSource(that returns a parent/children of an element) for the file browser
|
||||
*/
|
||||
export class FileBrowserDataSource implements IDataSource {
|
||||
|
||||
constructor(
|
||||
@IFileBrowserService private _fileBrowserService: IFileBrowserService
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the unique identifier of the given element.
|
||||
* No more than one element may use a given identifier.
|
||||
*/
|
||||
public getId(tree: ITree, element: any): string {
|
||||
if (element instanceof FileNode) {
|
||||
return (<FileNode>element).id;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a boolean value indicating whether the element has children.
|
||||
*/
|
||||
public hasChildren(tree: ITree, element: any): boolean {
|
||||
if (element instanceof FileNode) {
|
||||
return (<FileNode>element).hasChildren;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the element's children as an array in a promise.
|
||||
*/
|
||||
public getChildren(tree: ITree, element: any): TPromise<any> {
|
||||
return new TPromise<any>((resolve) => {
|
||||
if (element instanceof FileNode) {
|
||||
var node = <FileNode>element;
|
||||
if (node.children) {
|
||||
resolve(node.children);
|
||||
} else {
|
||||
this._fileBrowserService.expandFolderNode(node).then((nodeChildren) => {
|
||||
resolve(nodeChildren);
|
||||
}, expandError => {
|
||||
resolve([]);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
resolve([]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the element's parent in a promise.
|
||||
*/
|
||||
public getParent(tree: ITree, element: any): TPromise<any> {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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!sql/media/icons/common-icons';
|
||||
import 'vs/css!./media/fileBrowserDialog';
|
||||
import { Button } from 'sql/base/browser/ui/button/button';
|
||||
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox';
|
||||
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox';
|
||||
import * as DialogHelper from 'sql/workbench/browser/modal/dialogHelper';
|
||||
import { Modal } from 'sql/workbench/browser/modal/modal';
|
||||
import { attachModalDialogStyler, attachButtonStyler } from 'sql/platform/theme/common/styler';
|
||||
import * as TelemetryKeys from 'sql/common/telemetryKeys';
|
||||
import { FileNode } from 'sql/workbench/services/fileBrowser/common/fileNode';
|
||||
import { FileBrowserTreeView } from 'sql/workbench/services/fileBrowser/browser/fileBrowserTreeView';
|
||||
import { FileBrowserViewModel } from 'sql/workbench/services/fileBrowser/common/fileBrowserViewModel';
|
||||
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { Builder } from 'vs/base/browser/builder';
|
||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { MessageType } from 'vs/base/browser/ui/inputbox/inputBox';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { attachInputBoxStyler, attachSelectBoxStyler } from 'vs/platform/theme/common/styler';
|
||||
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
import { IPartService } from 'vs/workbench/services/part/common/partService';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { IClipboardService } from 'sql/platform/clipboard/common/clipboardService';
|
||||
|
||||
export class FileBrowserDialog extends Modal {
|
||||
private _viewModel: FileBrowserViewModel;
|
||||
private _bodyBuilder: Builder;
|
||||
private _filePathInputBox: InputBox;
|
||||
private _fileFilterSelectBox: SelectBox;
|
||||
private _okButton: Button;
|
||||
private _cancelButton: Button;
|
||||
private _onOk = new Emitter<string>();
|
||||
public onOk: Event<string> = this._onOk.event;
|
||||
|
||||
private _treeContainer: Builder;
|
||||
private _fileBrowserTreeView: FileBrowserTreeView;
|
||||
private _selectedFilePath: string;
|
||||
private _isFolderSelected: boolean;
|
||||
|
||||
constructor(title: string,
|
||||
@IPartService partService: IPartService,
|
||||
@IWorkbenchThemeService private _workbenchthemeService: IWorkbenchThemeService,
|
||||
@IInstantiationService private _instantiationService: IInstantiationService,
|
||||
@IContextViewService private _contextViewService: IContextViewService,
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IClipboardService clipboardService: IClipboardService
|
||||
) {
|
||||
super(title, TelemetryKeys.Backup, partService, telemetryService, clipboardService, _workbenchthemeService, contextKeyService, { isFlyout: true, hasTitleIcon: false, hasBackButton: true, hasSpinner: true });
|
||||
this._viewModel = this._instantiationService.createInstance(FileBrowserViewModel);
|
||||
this._viewModel.onAddFileTree(args => this.handleOnAddFileTree(args.rootNode, args.selectedNode, args.expandedNodes));
|
||||
this._viewModel.onPathValidate(args => this.handleOnValidate(args.succeeded, args.message));
|
||||
}
|
||||
|
||||
protected layout(height?: number): void {
|
||||
}
|
||||
|
||||
protected renderBody(container: HTMLElement) {
|
||||
new Builder(container).div({ 'class': 'file-browser-dialog' }, (bodyBuilder) => {
|
||||
this._bodyBuilder = bodyBuilder;
|
||||
});
|
||||
}
|
||||
|
||||
public render() {
|
||||
super.render();
|
||||
attachModalDialogStyler(this, this._themeService);
|
||||
|
||||
if (this.backButton) {
|
||||
|
||||
this.backButton.onDidClick(() => {
|
||||
this.close();
|
||||
});
|
||||
|
||||
this._register(attachButtonStyler(this.backButton, this._themeService, { buttonBackground: SIDE_BAR_BACKGROUND, buttonHoverBackground: SIDE_BAR_BACKGROUND }));
|
||||
}
|
||||
|
||||
this._bodyBuilder.div({ class: 'tree-view' }, (treeContainer) => {
|
||||
this._treeContainer = treeContainer;
|
||||
});
|
||||
|
||||
this._bodyBuilder.div({ class: 'option-section' }, (tableWrapper) => {
|
||||
tableWrapper.element('table', { class: 'file-table-content' }, (tableContainer) => {
|
||||
let pathLabel = localize('filebrowser.filepath', 'Selected path');
|
||||
let pathBuilder = DialogHelper.appendRow(tableContainer, pathLabel, 'file-input-label', 'file-input-box');
|
||||
this._filePathInputBox = new InputBox(pathBuilder.getHTMLElement(), this._contextViewService, {
|
||||
ariaLabel: pathLabel
|
||||
});
|
||||
|
||||
this._fileFilterSelectBox = new SelectBox(['*'], '*', this._contextViewService);
|
||||
let filterLabel = localize('fileFilter', 'Files of type');
|
||||
let filterBuilder = DialogHelper.appendRow(tableContainer, filterLabel, 'file-input-label', 'file-input-box');
|
||||
DialogHelper.appendInputSelectBox(filterBuilder, this._fileFilterSelectBox);
|
||||
});
|
||||
});
|
||||
|
||||
this._okButton = this.addFooterButton(localize('fileBrowser.ok', 'OK'), () => this.ok());
|
||||
this._okButton.enabled = false;
|
||||
this._cancelButton = this.addFooterButton(localize('fileBrowser.discard', 'Discard'), () => this.close());
|
||||
|
||||
this.registerListeners();
|
||||
this.updateTheme();
|
||||
}
|
||||
|
||||
public open(ownerUri: string,
|
||||
expandPath: string,
|
||||
fileFilters: [{ label: string, filters: string[] }],
|
||||
fileValidationServiceType: string,
|
||||
) {
|
||||
this._viewModel.initialize(ownerUri, expandPath, fileFilters, fileValidationServiceType);
|
||||
this._fileFilterSelectBox.setOptions(this._viewModel.formattedFileFilters);
|
||||
this._fileFilterSelectBox.select(0);
|
||||
this._filePathInputBox.value = expandPath;
|
||||
this._isFolderSelected = true;
|
||||
this.enableOkButton();
|
||||
this.showSpinner();
|
||||
this.show();
|
||||
|
||||
this._fileBrowserTreeView = this._instantiationService.createInstance(FileBrowserTreeView);
|
||||
this._fileBrowserTreeView.setOnClickedCallback((arg) => this.onClicked(arg));
|
||||
this._fileBrowserTreeView.setOnDoubleClickedCallback((arg) => this.onDoubleClicked(arg));
|
||||
this._viewModel.openFileBrowser(0, false);
|
||||
}
|
||||
|
||||
/* enter key */
|
||||
protected onAccept() {
|
||||
if (this._okButton.enabled === true) {
|
||||
this.ok();
|
||||
}
|
||||
}
|
||||
|
||||
private handleOnAddFileTree(rootNode: FileNode, selectedNode: FileNode, expandedNodes: FileNode[]) {
|
||||
this.updateFileTree(rootNode, selectedNode, expandedNodes);
|
||||
this.hideSpinner();
|
||||
}
|
||||
|
||||
private enableOkButton() {
|
||||
if (strings.isFalsyOrWhitespace(this._selectedFilePath) || this._isFolderSelected === true) {
|
||||
this._okButton.enabled = false;
|
||||
} else {
|
||||
this._okButton.enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private onClicked(selectedNode: FileNode) {
|
||||
this._filePathInputBox.value = selectedNode.fullPath;
|
||||
|
||||
if (selectedNode.isFile === true) {
|
||||
this._isFolderSelected = false;
|
||||
} else {
|
||||
this._isFolderSelected = true;
|
||||
}
|
||||
|
||||
this.enableOkButton();
|
||||
}
|
||||
|
||||
private onDoubleClicked(selectedNode: FileNode) {
|
||||
if (selectedNode.isFile === true) {
|
||||
this.ok();
|
||||
}
|
||||
}
|
||||
|
||||
private onFilePathChange(filePath: string) {
|
||||
this._isFolderSelected = false;
|
||||
this._selectedFilePath = filePath;
|
||||
|
||||
this._filePathInputBox.hideMessage();
|
||||
this.enableOkButton();
|
||||
}
|
||||
|
||||
private onFilePathBlur(param) {
|
||||
if (!strings.isFalsyOrWhitespace(param.value)) {
|
||||
this._viewModel.validateFilePaths([param.value]);
|
||||
}
|
||||
}
|
||||
|
||||
private ok() {
|
||||
this._onOk.fire(this._selectedFilePath);
|
||||
this.close();
|
||||
}
|
||||
|
||||
private handleOnValidate(succeeded: boolean, errorMessage: string) {
|
||||
if (succeeded === false) {
|
||||
if (strings.isFalsyOrWhitespace(errorMessage)) {
|
||||
errorMessage = 'The provided path is invalid.';
|
||||
}
|
||||
this._filePathInputBox.showMessage({ type: MessageType.ERROR, content: errorMessage });
|
||||
}
|
||||
}
|
||||
|
||||
private close() {
|
||||
if (this._fileBrowserTreeView) {
|
||||
this._fileBrowserTreeView.dispose();
|
||||
}
|
||||
this._onOk.dispose();
|
||||
this.hide();
|
||||
this._viewModel.closeFileBrowser();
|
||||
}
|
||||
|
||||
private updateFileTree(rootNode: FileNode, selectedNode: FileNode, expandedNodes: FileNode[]): void {
|
||||
this._fileBrowserTreeView.renderBody(this._treeContainer.getHTMLElement(), rootNode, selectedNode, expandedNodes);
|
||||
this._fileBrowserTreeView.setVisible(true);
|
||||
this._fileBrowserTreeView.layout(DOM.getTotalHeight(this._treeContainer.getHTMLElement()));
|
||||
}
|
||||
|
||||
private onFilterSelectChanged(filterIndex) {
|
||||
this.showSpinner();
|
||||
this._viewModel.openFileBrowser(filterIndex, true);
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this._register(this._fileFilterSelectBox.onDidSelect(selection => {
|
||||
this.onFilterSelectChanged(selection.index);
|
||||
}));
|
||||
this._register(this._filePathInputBox.onDidChange(e => {
|
||||
this.onFilePathChange(e);
|
||||
}));
|
||||
this._register(this._filePathInputBox.onLoseFocus(params => {
|
||||
this.onFilePathBlur(params);
|
||||
}));
|
||||
|
||||
// Theme styler
|
||||
this._register(attachInputBoxStyler(this._filePathInputBox, this._themeService));
|
||||
this._register(attachSelectBoxStyler(this._fileFilterSelectBox, this._themeService));
|
||||
this._register(attachButtonStyler(this._okButton, this._themeService));
|
||||
this._register(attachButtonStyler(this._cancelButton, this._themeService));
|
||||
|
||||
this._register(this._workbenchthemeService.onDidColorThemeChange(e => this.updateTheme()));
|
||||
}
|
||||
|
||||
// Update theming that is specific to file browser
|
||||
private updateTheme(): void {
|
||||
if (this._treeContainer) {
|
||||
this._treeContainer.style('background-color', this.headerAndFooterBackground);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { IFileBrowserDialogController } from 'sql/workbench/services/fileBrowser/common/fileBrowserDialogController';
|
||||
import { FileBrowserDialog } from 'sql/workbench/services/fileBrowser/browser/fileBrowserDialog';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
/**
|
||||
* File browser dialog service
|
||||
*/
|
||||
export class FileBrowserDialogController implements IFileBrowserDialogController {
|
||||
_serviceBrand: any;
|
||||
private _fileBrowserDialog: FileBrowserDialog;
|
||||
|
||||
constructor(
|
||||
@IInstantiationService private _instantiationService: IInstantiationService
|
||||
) {
|
||||
}
|
||||
|
||||
public showDialog(ownerUri: string,
|
||||
expandPath: string,
|
||||
fileFilters: [{ label: string, filters: string[] }],
|
||||
fileValidationServiceType: string,
|
||||
isWide: boolean,
|
||||
handleOnOk: (path: string) => void
|
||||
) {
|
||||
if (!this._fileBrowserDialog) {
|
||||
this._fileBrowserDialog = this._instantiationService.createInstance(FileBrowserDialog, localize('filebrowser.selectFile', "Select a file"));
|
||||
this._fileBrowserDialog.render();
|
||||
}
|
||||
|
||||
this._fileBrowserDialog.setWide(isWide);
|
||||
this._fileBrowserDialog.onOk((filepath) => handleOnOk(filepath));
|
||||
this._fileBrowserDialog.open(ownerUri, expandPath, fileFilters, fileValidationServiceType);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { FileNode } from 'sql/workbench/services/fileBrowser/common/fileNode';
|
||||
import { ITree, IRenderer } from 'vs/base/parts/tree/browser/tree';
|
||||
import { FileKind } from 'vs/platform/files/common/files';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { FileLabel } from 'vs/workbench/browser/labels';
|
||||
import { IFileTemplateData } from 'vs/workbench/parts/files/electron-browser/views/explorerViewer';
|
||||
import { toDisposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
const EmptyDisposable = toDisposable(() => null);
|
||||
|
||||
/**
|
||||
* Renders the tree items.
|
||||
* Uses the dom template to render file browser.
|
||||
*/
|
||||
export class FileBrowserRenderer implements IRenderer {
|
||||
public static readonly FILE_HEIGHT = 22;
|
||||
private static readonly FILE_TEMPLATE_ID = 'carbonFileBrowser';
|
||||
|
||||
constructor(
|
||||
@IInstantiationService private instantiationService: IInstantiationService
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the element's height in the tree, in pixels.
|
||||
*/
|
||||
public getHeight(tree: ITree, element: any): number {
|
||||
return FileBrowserRenderer.FILE_HEIGHT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a template ID for a given element.
|
||||
*/
|
||||
public getTemplateId(tree: ITree, element: any): string {
|
||||
return FileBrowserRenderer.FILE_TEMPLATE_ID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render template in a dom element based on template id
|
||||
*/
|
||||
public renderTemplate(tree: ITree, templateId: string, container: HTMLElement): IFileTemplateData {
|
||||
const elementDisposable = EmptyDisposable;
|
||||
const label = this.instantiationService.createInstance(FileLabel, container, void 0);
|
||||
return { elementDisposable, label, container };
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a element, given an object bag returned by the template
|
||||
*/
|
||||
public renderElement(tree: ITree, element: FileNode, templateId: string, templateData: IFileTemplateData): void {
|
||||
if (element) {
|
||||
templateData.label.element.style.display = 'flex';
|
||||
const extraClasses = ['explorer-item'];
|
||||
|
||||
var fileuri = URI.file(element.fullPath);
|
||||
var filekind;
|
||||
if (element.parent === null) {
|
||||
filekind = FileKind.ROOT_FOLDER;
|
||||
} else if (element.isFile === false) {
|
||||
filekind = FileKind.FOLDER;
|
||||
} else {
|
||||
filekind = FileKind.FILE;
|
||||
}
|
||||
|
||||
templateData.label.setFile(fileuri, { hidePath: true, fileKind: filekind, extraClasses });
|
||||
}
|
||||
}
|
||||
|
||||
public disposeTemplate(tree: ITree, templateId: string, templateData: IFileTemplateData): void {
|
||||
templateData.label.dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { FileBrowserDataSource } from 'sql/workbench/services/fileBrowser/browser/fileBrowserDataSource';
|
||||
import { FileBrowserController } from 'sql/workbench/services/fileBrowser/browser/fileBrowserController';
|
||||
import { FileBrowserRenderer } from 'sql/workbench/services/fileBrowser/browser/fileBrowserRenderer';
|
||||
import { IFileBrowserService } from 'sql/platform/fileBrowser/common/interfaces';
|
||||
import { FileNode } from 'sql/workbench/services/fileBrowser/common/fileNode';
|
||||
import errors = require('vs/base/common/errors');
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import nls = require('vs/nls');
|
||||
import { DefaultFilter, DefaultAccessibilityProvider, DefaultDragAndDrop } from 'vs/base/parts/tree/browser/treeDefaults';
|
||||
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { attachListStyler } from 'vs/platform/theme/common/styler';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { ITree } from 'vs/base/parts/tree/browser/tree';
|
||||
|
||||
/**
|
||||
* Implements tree view for file browser
|
||||
*/
|
||||
export class FileBrowserTreeView implements IDisposable {
|
||||
private _tree: ITree;
|
||||
private _toDispose: IDisposable[] = [];
|
||||
|
||||
constructor(
|
||||
@IInstantiationService private _instantiationService: IInstantiationService,
|
||||
@IFileBrowserService private _fileBrowserService: IFileBrowserService,
|
||||
@IThemeService private _themeService: IThemeService
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the view body
|
||||
*/
|
||||
public renderBody(container: HTMLElement, rootNode: FileNode, selectedNode: FileNode, expandedNodes: FileNode[]): void {
|
||||
if (!this._tree) {
|
||||
DOM.addClass(container, 'show-file-icons');
|
||||
this._tree = this.createFileBrowserTree(container, this._instantiationService);
|
||||
this._toDispose.push(this._tree.onDidChangeSelection((event) => this.onSelected(event)));
|
||||
this._toDispose.push(this._fileBrowserService.onExpandFolder(fileNode => this._tree.refresh(fileNode)));
|
||||
this._toDispose.push(attachListStyler(this._tree, this._themeService));
|
||||
this._tree.domFocus();
|
||||
}
|
||||
|
||||
if (rootNode) {
|
||||
this._tree.setInput(rootNode).then(() => {
|
||||
if (expandedNodes) {
|
||||
this._tree.expandAll(expandedNodes);
|
||||
}
|
||||
if (selectedNode) {
|
||||
this._tree.select(selectedNode);
|
||||
this._tree.setFocus(selectedNode);
|
||||
}
|
||||
this._tree.getFocus();
|
||||
}, errors.onUnexpectedError);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a file browser tree
|
||||
*/
|
||||
public createFileBrowserTree(treeContainer: HTMLElement, instantiationService: IInstantiationService): Tree {
|
||||
const dataSource = instantiationService.createInstance(FileBrowserDataSource);
|
||||
const renderer = instantiationService.createInstance(FileBrowserRenderer);
|
||||
const controller = instantiationService.createInstance(FileBrowserController);
|
||||
const dnd = new DefaultDragAndDrop();
|
||||
const filter = new DefaultFilter();
|
||||
const sorter = null;
|
||||
const accessibilityProvider = new DefaultAccessibilityProvider();
|
||||
|
||||
return new Tree(treeContainer, {
|
||||
dataSource, renderer, controller, dnd, filter, sorter, accessibilityProvider
|
||||
}, {
|
||||
indentPixels: 10,
|
||||
twistiePixels: 12,
|
||||
ariaLabel: nls.localize({ key: 'fileBrowser.regTreeAriaLabel', comment: ['FileBrowserTree'] }, 'File browser tree')
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the tree
|
||||
*/
|
||||
public refreshTree(rootNode: FileNode): void {
|
||||
let selectedElement: any;
|
||||
let targetsToExpand: any[];
|
||||
|
||||
// Focus
|
||||
this._tree.domFocus();
|
||||
|
||||
if (this._tree) {
|
||||
let selection = this._tree.getSelection();
|
||||
if (selection && selection.length === 1) {
|
||||
selectedElement = <any>selection[0];
|
||||
}
|
||||
targetsToExpand = this._tree.getExpandedElements();
|
||||
}
|
||||
|
||||
if (rootNode) {
|
||||
this._tree.setInput(rootNode).then(() => {
|
||||
// Make sure to expand all folders that were expanded in the previous session
|
||||
if (targetsToExpand) {
|
||||
this._tree.expandAll(targetsToExpand);
|
||||
}
|
||||
if (selectedElement) {
|
||||
this._tree.select(selectedElement);
|
||||
this._tree.setFocus(selectedElement);
|
||||
}
|
||||
this._tree.getFocus();
|
||||
}, errors.onUnexpectedError);
|
||||
}
|
||||
}
|
||||
|
||||
private onSelected(event: any) {
|
||||
let selection = this._tree.getSelection();
|
||||
|
||||
if (selection && selection.length > 0 && (selection[0] instanceof FileNode)) {
|
||||
let isMouseOrigin = event.payload && (event.payload.origin === 'mouse');
|
||||
let isSingleClick = isMouseOrigin && event.payload.originalEvent && event.payload.originalEvent.detail === 1;
|
||||
let isDoubleClick = isMouseOrigin && event.payload.originalEvent && event.payload.originalEvent.detail === 2;
|
||||
if (isSingleClick) {
|
||||
this.onClickedCallback(event.selection[0]);
|
||||
} else if (isDoubleClick) {
|
||||
this.onDoublieClickedCallback(event.selection[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public onClickedCallback: any;
|
||||
public setOnClickedCallback(fn: any) {
|
||||
this.onClickedCallback = fn;
|
||||
}
|
||||
|
||||
public onDoublieClickedCallback: any;
|
||||
public setOnDoubleClickedCallback(fn: any) {
|
||||
this.onDoublieClickedCallback = fn;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the layout of the view
|
||||
*/
|
||||
public layout(height?: number): void {
|
||||
this._tree.layout(height);
|
||||
}
|
||||
|
||||
/**
|
||||
* set the visibility of the view
|
||||
*/
|
||||
public setVisible(visible: boolean): void {
|
||||
if (visible) {
|
||||
this._tree.onVisible();
|
||||
} else {
|
||||
this._tree.onHidden();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* dispose the file browser tree view
|
||||
*/
|
||||
public dispose(): void {
|
||||
if (this._tree) {
|
||||
this._tree.dispose();
|
||||
}
|
||||
this._toDispose = dispose(this._toDispose);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.file-browser-dialog {
|
||||
height: 100%;
|
||||
padding-top: 12px;
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.file-browser-dialog .tree-view {
|
||||
height: calc(100% - 90px);
|
||||
}
|
||||
|
||||
.file-table-content {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.file-browser-dialog .option-section {
|
||||
padding-top: 10px;
|
||||
height: 90px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.file-input-label {
|
||||
width: 50px;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.file-input-box {
|
||||
width: 200px;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.file-browser-dialog .explorer-item {
|
||||
height: 22px;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.file-browser-dialog .show-file-icons .monaco-tree-row .content {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.file-browser-dialog .monaco-tree .monaco-tree-rows.show-twisties > .monaco-tree-row.has-children > .content:before {
|
||||
background-size: 16px;
|
||||
background-position: 50% 50%;
|
||||
background-repeat: no-repeat;
|
||||
padding-right: 6px;
|
||||
width: 16px;
|
||||
height: 22px;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
content: ' ';
|
||||
position: initial;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
export const IFileBrowserDialogController = createDecorator<IFileBrowserDialogController>('fileBrowserDialogService');
|
||||
export interface IFileBrowserDialogController {
|
||||
_serviceBrand: any;
|
||||
/**
|
||||
* Show file browser dialog
|
||||
*/
|
||||
showDialog(ownerUri: string,
|
||||
expandPath: string,
|
||||
fileFilters: { label: string, filters: string[] }[],
|
||||
fileValidationServiceType: string,
|
||||
isWide: boolean,
|
||||
handleOnOk: (path: string) => void): void;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { FileNode } from 'sql/workbench/services/fileBrowser/common/fileNode';
|
||||
|
||||
/**
|
||||
* File tree info needed to render initially
|
||||
*/
|
||||
export class FileBrowserTree {
|
||||
public rootNode: FileNode;
|
||||
public selectedNode: FileNode;
|
||||
public expandedNodes: FileNode[];
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { IFileBrowserService } from 'sql/platform/fileBrowser/common/interfaces';
|
||||
import { localize } from 'vs/nls';
|
||||
|
||||
/**
|
||||
* View model for file browser dialog
|
||||
*/
|
||||
export class FileBrowserViewModel {
|
||||
private _ownerUri: string;
|
||||
private _expandPath: string;
|
||||
private _fileFilters: [{ label: string, filters: string[] }];
|
||||
private _fileValidationServiceType: string;
|
||||
public formattedFileFilters: string[];
|
||||
|
||||
constructor( @IFileBrowserService private _fileBrowserService: IFileBrowserService) {
|
||||
}
|
||||
|
||||
public onAddFileTree(onAddFileTreeCallback) {
|
||||
this._fileBrowserService.onAddFileTree(args => onAddFileTreeCallback(args));
|
||||
}
|
||||
|
||||
public onPathValidate(onPathValidateCallback) {
|
||||
this._fileBrowserService.onPathValidate(args => onPathValidateCallback(args));
|
||||
}
|
||||
|
||||
public initialize(ownerUri: string,
|
||||
expandPath: string,
|
||||
fileFilters: [{ label: string, filters: string[] }],
|
||||
fileValidationServiceType: string,
|
||||
) {
|
||||
this._ownerUri = ownerUri;
|
||||
this._expandPath = expandPath;
|
||||
this._fileValidationServiceType = fileValidationServiceType;
|
||||
|
||||
if (!fileFilters) {
|
||||
this._fileFilters = [{ label: localize('allFiles', 'All files'), filters: ['*'] }];
|
||||
} else {
|
||||
this._fileFilters = fileFilters;
|
||||
}
|
||||
this.formattedFileFilters = [];
|
||||
for (var i = 0; i < this._fileFilters.length; i++) {
|
||||
var filterStr = this._fileFilters[i].label + '(' + this._fileFilters[i].filters.join(';') + ')';
|
||||
this.formattedFileFilters.push(filterStr);
|
||||
}
|
||||
}
|
||||
|
||||
public validateFilePaths(selectedFiles: string[]) {
|
||||
this._fileBrowserService.validateFilePaths(this._ownerUri, this._fileValidationServiceType, selectedFiles);
|
||||
}
|
||||
|
||||
public openFileBrowser(filterIndex: number, changeFilter: boolean) {
|
||||
if (this._fileFilters[filterIndex]) {
|
||||
this._fileBrowserService.openFileBrowser(this._ownerUri, this._expandPath, this._fileFilters[filterIndex].filters, changeFilter);
|
||||
}
|
||||
}
|
||||
|
||||
public closeFileBrowser() {
|
||||
this._fileBrowserService.closeFileBrowser(this._ownerUri);
|
||||
}
|
||||
}
|
||||
72
src/sql/workbench/services/fileBrowser/common/fileNode.ts
Normal file
72
src/sql/workbench/services/fileBrowser/common/fileNode.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { generateUuid } from 'vs/base/common/uuid';
|
||||
|
||||
/**
|
||||
* File/folder node in file browser
|
||||
* FileTreeNode is converted to this FileNode for UI interactions
|
||||
*/
|
||||
export class FileNode {
|
||||
/**
|
||||
* Node id
|
||||
*/
|
||||
public id: string;
|
||||
|
||||
/**
|
||||
* Connection uri
|
||||
*/
|
||||
public ownerUri: string;
|
||||
|
||||
/**
|
||||
* File or folder name
|
||||
*/
|
||||
public name: string;
|
||||
|
||||
/**
|
||||
* Full path of file or folder
|
||||
*/
|
||||
public fullPath: string;
|
||||
|
||||
/**
|
||||
* Parent node
|
||||
*/
|
||||
public parent: FileNode;
|
||||
|
||||
/**
|
||||
* Children nodes
|
||||
*/
|
||||
public children: FileNode[];
|
||||
|
||||
/**
|
||||
* Is the node expanded
|
||||
*/
|
||||
public isExpanded: boolean;
|
||||
|
||||
/**
|
||||
* Is the node file or folder
|
||||
*/
|
||||
public isFile: boolean;
|
||||
|
||||
/**
|
||||
* Does this node have children
|
||||
*/
|
||||
public hasChildren: boolean;
|
||||
|
||||
constructor(id: string, name: string, fullPath: string, isFile: boolean, isExpanded: boolean, ownerUri: string, parent: FileNode) {
|
||||
if (id) {
|
||||
this.id = id;
|
||||
} else {
|
||||
this.id = generateUuid();
|
||||
}
|
||||
|
||||
this.name = name;
|
||||
this.fullPath = fullPath;
|
||||
this.isFile = isFile;
|
||||
this.ownerUri = ownerUri;
|
||||
this.isExpanded = isExpanded;
|
||||
this.parent = parent;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* List of services that provide file validation callback to file browser service
|
||||
*/
|
||||
|
||||
export const backup: string = 'Backup';
|
||||
export const restore: string = 'Restore';
|
||||
Reference in New Issue
Block a user