window.ts 6.0 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5 6 7 8
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

'use strict';

import platform = require('vs/base/common/platform');
9
import URI from 'vs/base/common/uri';
J
Johannes Rieken 已提交
10 11
import { TPromise } from 'vs/base/common/winjs.base';
import { stat } from 'vs/base/node/pfs';
12
import DOM = require('vs/base/browser/dom');
13
import { extractResources } from 'vs/base/browser/dnd';
J
Johannes Rieken 已提交
14 15 16 17 18
import { Builder, $ } from 'vs/base/browser/builder';
import { IPartService } from 'vs/workbench/services/part/common/partService';
import { asFileEditorInput } from 'vs/workbench/common/editor';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
19
import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows';
B
Benjamin Pasero 已提交
20
import { ITitleService } from 'vs/workbench/services/title/common/titleService';
E
Erich Gamma 已提交
21

B
Benjamin Pasero 已提交
22
import { remote } from 'electron';
E
Erich Gamma 已提交
23

24 25
const dialog = remote.dialog;

E
Erich Gamma 已提交
26
export class ElectronWindow {
27
	private win: Electron.BrowserWindow;
28
	private windowId: number;
E
Erich Gamma 已提交
29 30

	constructor(
31
		win: Electron.BrowserWindow,
E
Erich Gamma 已提交
32 33
		shellContainer: HTMLElement,
		@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
34
		@IEditorGroupService private editorGroupService: IEditorGroupService,
35
		@IPartService private partService: IPartService,
36
		@IWindowsService private windowsService: IWindowsService,
B
Benjamin Pasero 已提交
37 38
		@IWindowService private windowService: IWindowService,
		@ITitleService private titleService: ITitleService
E
Erich Gamma 已提交
39 40
	) {
		this.win = win;
41
		this.windowId = win.id;
B
Benjamin Pasero 已提交
42

E
Erich Gamma 已提交
43 44 45 46 47 48 49
		this.registerListeners();
	}

	private registerListeners(): void {

		// React to editor input changes (Mac only)
		if (platform.platform === platform.Platform.Mac) {
50
			this.editorGroupService.onEditorsChanged(() => {
B
Benjamin Pasero 已提交
51
				const fileInput = asFileEditorInput(this.editorService.getActiveEditorInput(), true);
52
				const fileName = fileInput ? fileInput.getResource().fsPath : '';
E
Erich Gamma 已提交
53

B
Benjamin Pasero 已提交
54
				this.titleService.setRepresentedFilename(fileName);
E
Erich Gamma 已提交
55 56 57
			});
		}

58 59 60 61 62
		let draggedExternalResources: URI[];
		let dropOverlay: Builder;

		function cleanUp(): void {
			draggedExternalResources = void 0;
63

64 65 66 67 68 69 70 71 72 73 74
			if (dropOverlay) {
				dropOverlay.destroy();
				dropOverlay = void 0;
			}
		}

		// Detect resources dropped into Code from outside
		window.document.body.addEventListener(DOM.EventType.DRAG_OVER, (e: DragEvent) => {
			DOM.EventHelper.stop(e);

			if (!draggedExternalResources) {
75
				draggedExternalResources = extractResources(e, true /* external only */).map(d => d.resource);
76

77 78 79 80 81 82 83 84 85
				// Find out if folders are dragged and show the appropiate feedback then
				this.includesFolder(draggedExternalResources).done(includesFolder => {
					if (includesFolder) {
						dropOverlay = $(window.document.getElementById(this.partService.getWorkbenchElementId()))
							.div({ id: 'monaco-workbench-drop-overlay' })
							.on(DOM.EventType.DROP, (e: DragEvent) => {
								DOM.EventHelper.stop(e, true);

								this.focus(); // make sure this window has focus so that the open call reaches the right window!
86
								this.windowsService.windowOpen(draggedExternalResources.map(r => r.fsPath));
87

B
Benjamin Pasero 已提交
88
								cleanUp();
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
							})
							.on([DOM.EventType.DRAG_LEAVE, DOM.EventType.DRAG_END], () => {
								cleanUp();
							}).once(DOM.EventType.MOUSE_OVER, () => {
								// Under some circumstances we have seen reports where the drop overlay is not being
								// cleaned up and as such the editor area remains under the overlay so that you cannot
								// type into the editor anymore. This seems related to using VMs and DND via host and
								// guest OS, though some users also saw it without VMs.
								// To protect against this issue we always destroy the overlay as soon as we detect a
								// mouse event over it. The delay is used to guarantee we are not interfering with the
								// actual DROP event that can also trigger a mouse over event.
								// See also: https://github.com/Microsoft/vscode/issues/10970
								setTimeout(() => {
									cleanUp();
								}, 300);
							});
					}
				});
107 108 109 110 111
			}
		});

		// Clear our map and overlay on any finish of DND outside the overlay
		[DOM.EventType.DROP, DOM.EventType.DRAG_END].forEach(event => {
112
			window.document.body.addEventListener(event, (e: DragEvent) => {
113
				if (!dropOverlay || e.target !== dropOverlay.getHTMLElement()) {
B
Benjamin Pasero 已提交
114
					cleanUp(); // only run cleanUp() if we are not over the overlay (because we are being called in capture phase)
115 116 117 118 119 120 121
				}
			}, true /* use capture because components within may preventDefault() when they accept the drop */);
		});

		// prevent opening a real URL inside the shell
		window.document.body.addEventListener(DOM.EventType.DROP, (e: DragEvent) => {
			DOM.EventHelper.stop(e);
E
Erich Gamma 已提交
122 123 124
		});

		// Handle window.open() calls
J
Joao Moreno 已提交
125
		const $this = this;
B
Benjamin Pasero 已提交
126
		(<any>window).open = function (url: string, target: string, features: string, replace: boolean) {
J
Joao Moreno 已提交
127
			$this.windowsService.openExternal(url);
B
Benjamin Pasero 已提交
128
			return null;
E
Erich Gamma 已提交
129 130 131
		};
	}

132 133 134 135
	private includesFolder(resources: URI[]): TPromise<boolean> {
		return TPromise.join(resources.map(resource => {
			return stat(resource.fsPath).then(stats => stats.isDirectory() ? true : false, error => false);
		})).then(res => res.some(res => !!res));
E
Erich Gamma 已提交
136 137 138 139 140 141
	}

	public close(): void {
		this.win.close();
	}

B
Benjamin Pasero 已提交
142
	public showMessageBox(options: Electron.ShowMessageBoxOptions): number {
143
		return dialog.showMessageBox(this.win, options);
E
Erich Gamma 已提交
144 145
	}

B
Benjamin Pasero 已提交
146
	public showSaveDialog(options: Electron.SaveDialogOptions, callback?: (fileName: string) => void): string {
147 148 149 150
		if (callback) {
			return dialog.showSaveDialog(this.win, options, callback);
		}

M
Martin Aeschlimann 已提交
151
		return dialog.showSaveDialog(this.win, options); // https://github.com/electron/electron/issues/4936
E
Erich Gamma 已提交
152 153
	}

B
Benjamin Pasero 已提交
154
	public focus(): TPromise<void> {
J
Joao Moreno 已提交
155
		return this.windowService.focusWindow();
E
Erich Gamma 已提交
156 157
	}
}