window.ts 6.5 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 10
import URI from 'vs/base/common/uri';
import DOM = require('vs/base/browser/dom');
11 12 13 14
import DND = require('vs/base/browser/dnd');
import {Builder, $} from 'vs/base/browser/builder';
import {Identifiers} from 'vs/workbench/common/constants';
import {asFileEditorInput} from 'vs/workbench/common/editor';
E
Erich Gamma 已提交
15 16 17 18
import {IViewletService} from 'vs/workbench/services/viewlet/common/viewletService';
import {IWorkbenchEditorService} from 'vs/workbench/services/editor/common/editorService';
import {IStorageService} from 'vs/platform/storage/common/storage';
import {IEventService} from 'vs/platform/event/common/event';
19
import {IEditorGroupService} from 'vs/workbench/services/group/common/groupService';
E
Erich Gamma 已提交
20

21
import {ipcRenderer as ipc, shell, remote} from 'electron';
22 23
import * as fs from 'fs';
import * as path from 'path';
E
Erich Gamma 已提交
24

25 26
const dialog = remote.dialog;

B
Benjamin Pasero 已提交
27 28 29 30
export interface IWindowConfiguration {
	window: {
		openFilesInNewWindow: boolean;
		reopenFolders: string;
31
		restoreFullscreen: boolean;
B
Benjamin Pasero 已提交
32
		zoomLevel: number;
B
Benjamin Pasero 已提交
33
	};
B
Benjamin Pasero 已提交
34 35
}

36 37 38 39 40 41 42
enum DraggedFileType {
	UNKNOWN,
	FILE,
	EXTENSION,
	FOLDER
}

E
Erich Gamma 已提交
43
export class ElectronWindow {
44
	private win: Electron.BrowserWindow;
45
	private windowId: number;
E
Erich Gamma 已提交
46 47

	constructor(
48
		win: Electron.BrowserWindow,
E
Erich Gamma 已提交
49 50 51 52
		shellContainer: HTMLElement,
		@IEventService private eventService: IEventService,
		@IStorageService private storageService: IStorageService,
		@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
53
		@IEditorGroupService private editorGroupService: IEditorGroupService,
E
Erich Gamma 已提交
54 55 56
		@IViewletService private viewletService: IViewletService
	) {
		this.win = win;
57
		this.windowId = win.id;
E
Erich Gamma 已提交
58 59 60 61 62 63 64
		this.registerListeners();
	}

	private registerListeners(): void {

		// React to editor input changes (Mac only)
		if (platform.platform === platform.Platform.Mac) {
65
			this.editorGroupService.onEditorsChanged(() => {
B
Benjamin Pasero 已提交
66
				const fileInput = asFileEditorInput(this.editorService.getActiveEditorInput(), true);
67 68 69 70
				let representedFilename = '';
				if (fileInput) {
					representedFilename = fileInput.getResource().fsPath;
				}
E
Erich Gamma 已提交
71

72
				ipc.send('vscode:setRepresentedFilename', this.windowId, representedFilename);
E
Erich Gamma 已提交
73 74 75
			});
		}

76 77 78 79 80
		let draggedExternalResources: URI[];
		let dropOverlay: Builder;

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

82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
			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 */);

				// Show Code wide overlay if we detect a Folder or Extension to be dragged
				if (draggedExternalResources.some(r => {
					const kind = this.getFileKind(r);

					return kind === DraggedFileType.FOLDER || kind === DraggedFileType.EXTENSION;
				})) {
					dropOverlay = $(window.document.getElementById(Identifiers.WORKBENCH_CONTAINER))
						.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

							cleanUp();
						})
						.on([DOM.EventType.DRAG_LEAVE, DOM.EventType.DRAG_END], () => {
							cleanUp();
						});
				}
			}
		});

		// Clear our map and overlay on any finish of DND outside the overlay
		[DOM.EventType.DROP, DOM.EventType.DRAG_END].forEach(event => {
120
			window.document.body.addEventListener(event, (e: DragEvent) => {
121
				if (!dropOverlay || e.target !== dropOverlay.getHTMLElement()) {
B
Benjamin Pasero 已提交
122
					cleanUp(); // only run cleanUp() if we are not over the overlay (because we are being called in capture phase)
123 124 125 126 127 128 129
				}
			}, 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 已提交
130 131 132
		});

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

B
Benjamin Pasero 已提交
136
			return null;
E
Erich Gamma 已提交
137
		};
138 139 140 141

		// Patch focus to also focus the entire window
		const originalFocus = window.focus;
		const $this = this;
142
		window.focus = function () {
143 144 145
			originalFocus.call(this, arguments);
			$this.focus();
		};
E
Erich Gamma 已提交
146 147
	}

148 149 150 151 152 153 154 155 156 157
	private getFileKind(resource: URI): DraggedFileType {
		if (path.extname(resource.fsPath) === '.vsix') {
			return DraggedFileType.EXTENSION;
		}

		let kind = DraggedFileType.UNKNOWN;
		try {
			kind = fs.statSync(resource.fsPath).isDirectory() ? DraggedFileType.FOLDER : DraggedFileType.FILE;
		} catch (error) {
			// Do not fail in DND handler
E
Erich Gamma 已提交
158 159
		}

160
		return kind;
E
Erich Gamma 已提交
161 162 163 164 165 166 167 168 169 170
	}

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

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

171
	public reload(): void {
172
		ipc.send('vscode:reloadWindow', this.windowId);
173 174
	}

B
Benjamin Pasero 已提交
175
	public showMessageBox(options: Electron.ShowMessageBoxOptions): number {
176
		return dialog.showMessageBox(this.win, options);
E
Erich Gamma 已提交
177 178
	}

B
Benjamin Pasero 已提交
179
	public showSaveDialog(options: Electron.SaveDialogOptions, callback?: (fileName: string) => void): string {
180 181 182 183
		if (callback) {
			return dialog.showSaveDialog(this.win, options, callback);
		}

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

187 188
	public setFullScreen(fullscreen: boolean): void {
		ipc.send('vscode:setFullScreen', this.windowId, fullscreen); // handled from browser process
E
Erich Gamma 已提交
189 190
	}

191 192
	public openDevTools(): void {
		ipc.send('vscode:openDevTools', this.windowId); // handled from browser process
E
Erich Gamma 已提交
193 194 195
	}

	public setMenuBarVisibility(visible: boolean): void {
196
		ipc.send('vscode:setMenuBarVisibility', this.windowId, visible); // handled from browser process
E
Erich Gamma 已提交
197 198 199
	}

	public focus(): void {
200
		ipc.send('vscode:focusWindow', this.windowId); // handled from browser process
E
Erich Gamma 已提交
201 202 203
	}

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