window.ts 6.6 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';
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 14
import DND = require('vs/base/browser/dnd');
import {Builder, $} from 'vs/base/browser/builder';
15
import {IPartService} from 'vs/workbench/services/part/common/partService';
16
import {asFileEditorInput} from 'vs/workbench/common/editor';
E
Erich Gamma 已提交
17
import {IWorkbenchEditorService} from 'vs/workbench/services/editor/common/editorService';
18
import {IEditorGroupService} from 'vs/workbench/services/group/common/groupService';
E
Erich Gamma 已提交
19

20
import {ipcRenderer as ipc, shell, remote} from 'electron';
E
Erich Gamma 已提交
21

22 23
const dialog = remote.dialog;

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

	constructor(
29
		win: Electron.BrowserWindow,
E
Erich Gamma 已提交
30 31
		shellContainer: HTMLElement,
		@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
32
		@IEditorGroupService private editorGroupService: IEditorGroupService,
33
		@IPartService private partService: IPartService
E
Erich Gamma 已提交
34 35
	) {
		this.win = win;
36
		this.windowId = win.id;
E
Erich Gamma 已提交
37 38 39 40 41 42 43
		this.registerListeners();
	}

	private registerListeners(): void {

		// React to editor input changes (Mac only)
		if (platform.platform === platform.Platform.Mac) {
44
			this.editorGroupService.onEditorsChanged(() => {
B
Benjamin Pasero 已提交
45
				const fileInput = asFileEditorInput(this.editorService.getActiveEditorInput(), true);
46 47 48 49
				let representedFilename = '';
				if (fileInput) {
					representedFilename = fileInput.getResource().fsPath;
				}
E
Erich Gamma 已提交
50

51
				ipc.send('vscode:setRepresentedFilename', this.windowId, representedFilename);
E
Erich Gamma 已提交
52 53 54
			});
		}

55 56 57 58 59
		let draggedExternalResources: URI[];
		let dropOverlay: Builder;

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

61 62 63 64 65 66 67 68 69 70 71 72 73
			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) {
				draggedExternalResources = DND.extractResources(e, true /* external only */);

74 75 76 77 78 79 80 81 82 83 84
				// 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!
								ipc.send('vscode:windowOpen', draggedExternalResources.map(r => r.fsPath)); // handled from browser process

B
Benjamin Pasero 已提交
85
								cleanUp();
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
							})
							.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);
							});
					}
				});
104 105 106 107 108
			}
		});

		// Clear our map and overlay on any finish of DND outside the overlay
		[DOM.EventType.DROP, DOM.EventType.DRAG_END].forEach(event => {
109
			window.document.body.addEventListener(event, (e: DragEvent) => {
110
				if (!dropOverlay || e.target !== dropOverlay.getHTMLElement()) {
B
Benjamin Pasero 已提交
111
					cleanUp(); // only run cleanUp() if we are not over the overlay (because we are being called in capture phase)
112 113 114 115 116 117 118
				}
			}, 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 已提交
119 120 121
		});

		// Handle window.open() calls
B
Benjamin Pasero 已提交
122
		(<any>window).open = function (url: string, target: string, features: string, replace: boolean) {
B
Benjamin Pasero 已提交
123
			shell.openExternal(url);
E
Erich Gamma 已提交
124

B
Benjamin Pasero 已提交
125
			return null;
E
Erich Gamma 已提交
126
		};
127 128 129 130

		// Patch focus to also focus the entire window
		const originalFocus = window.focus;
		const $this = this;
131
		window.focus = function () {
132 133 134
			originalFocus.call(this, arguments);
			$this.focus();
		};
E
Erich Gamma 已提交
135 136
	}

137 138 139 140
	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 已提交
141 142 143 144 145 146 147 148 149 150
	}

	public openNew(): void {
		ipc.send('vscode:openNewWindow'); // handled from browser process
	}

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

151
	public reload(): void {
152
		ipc.send('vscode:reloadWindow', this.windowId);
153 154
	}

B
Benjamin Pasero 已提交
155
	public showMessageBox(options: Electron.ShowMessageBoxOptions): number {
156
		return dialog.showMessageBox(this.win, options);
E
Erich Gamma 已提交
157 158
	}

B
Benjamin Pasero 已提交
159
	public showSaveDialog(options: Electron.SaveDialogOptions, callback?: (fileName: string) => void): string {
160 161 162 163
		if (callback) {
			return dialog.showSaveDialog(this.win, options, callback);
		}

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

167 168
	public setFullScreen(fullscreen: boolean): void {
		ipc.send('vscode:setFullScreen', this.windowId, fullscreen); // handled from browser process
E
Erich Gamma 已提交
169 170
	}

171 172
	public openDevTools(): void {
		ipc.send('vscode:openDevTools', this.windowId); // handled from browser process
E
Erich Gamma 已提交
173 174 175
	}

	public setMenuBarVisibility(visible: boolean): void {
176
		ipc.send('vscode:setMenuBarVisibility', this.windowId, visible); // handled from browser process
E
Erich Gamma 已提交
177 178 179
	}

	public focus(): void {
180
		ipc.send('vscode:focusWindow', this.windowId); // handled from browser process
E
Erich Gamma 已提交
181 182 183
	}

	public flashFrame(): void {
184
		ipc.send('vscode:flashFrame', this.windowId); // handled from browser process
E
Erich Gamma 已提交
185 186
	}
}