windowsService.ts 10.5 KB
Newer Older
J
Joao Moreno 已提交
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 { TPromise } from 'vs/base/common/winjs.base';
9 10
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { assign } from 'vs/base/common/objects';
J
jordanmkasla2009 已提交
11
import URI from 'vs/base/common/uri';
J
Joao Moreno 已提交
12
import { IWindowsService } from 'vs/platform/windows/common/windows';
13
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
J
Joao Moreno 已提交
14
import { shell, crashReporter, app } from 'electron';
15
import Event, { chain } from 'vs/base/common/event';
J
Joao Moreno 已提交
16
import { fromEventEmitter } from 'vs/base/node/event';
17
import { IURLService } from 'vs/platform/url/common/url';
18
import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry';
J
Joao Moreno 已提交
19 20

// TODO@Joao: remove this dependency, move all implementation to this class
C
Christof Marti 已提交
21 22
import { OpenContext } from 'vs/code/common/windows';
import { IWindowsMainService } from 'vs/code/electron-main/windows';
23
import { ILifecycleService } from "vs/code/electron-main/lifecycle";
J
Joao Moreno 已提交
24

25
export interface ISharedProcess {
26
	whenReady(): TPromise<void>;
27 28 29
	toggle(): void;
}

30
export class WindowsService implements IWindowsService, IDisposable {
J
Joao Moreno 已提交
31 32 33

	_serviceBrand: any;

34 35
	private disposables: IDisposable[] = [];

J
Joao Moreno 已提交
36
	onWindowOpen: Event<number> = fromEventEmitter(app, 'browser-window-created', (_, w: Electron.BrowserWindow) => w.id);
J
Joao Moreno 已提交
37 38
	onWindowFocus: Event<number> = fromEventEmitter(app, 'browser-window-focus', (_, w: Electron.BrowserWindow) => w.id);

J
Joao Moreno 已提交
39
	constructor(
40
		private sharedProcess: ISharedProcess,
41
		@IWindowsMainService private windowsMainService: IWindowsMainService,
42
		@IEnvironmentService private environmentService: IEnvironmentService,
43 44
		@IURLService urlService: IURLService,
		@ILifecycleService private lifecycleService: ILifecycleService
45 46
	) {
		chain(urlService.onOpenURL)
47
			.filter(uri => uri.authority === 'file' && !!uri.path)
J
jordanmkasla2009 已提交
48
			.map(uri => this.parseURIForOpen(uri))
49
			.on(this.openFileForURI, this, this.disposables);
50
	}
J
Joao Moreno 已提交
51

52 53
	openFileFolderPicker(windowId: number, forceNewWindow?: boolean, data?: ITelemetryData): TPromise<void> {
		this.windowsMainService.openFileFolderPicker(forceNewWindow, data);
J
Joao Moreno 已提交
54 55 56
		return TPromise.as(null);
	}

57 58
	openFilePicker(windowId: number, forceNewWindow?: boolean, path?: string, data?: ITelemetryData): TPromise<void> {
		this.windowsMainService.openFilePicker(forceNewWindow, path, undefined, data);
J
Joao Moreno 已提交
59 60 61
		return TPromise.as(null);
	}

62
	openFolderPicker(windowId: number, forceNewWindow?: boolean, data?: ITelemetryData): TPromise<void> {
63
		const vscodeWindow = this.windowsMainService.getWindowById(windowId);
64
		this.windowsMainService.openFolderPicker(forceNewWindow, vscodeWindow, data);
65

J
Joao Moreno 已提交
66 67
		return TPromise.as(null);
	}
68 69 70 71 72 73 74 75 76 77

	reloadWindow(windowId: number): TPromise<void> {
		const vscodeWindow = this.windowsMainService.getWindowById(windowId);

		if (vscodeWindow) {
			this.windowsMainService.reload(vscodeWindow);
		}

		return TPromise.as(null);
	}
J
Joao Moreno 已提交
78

J
Joao Moreno 已提交
79 80 81 82 83 84 85 86 87 88
	openDevTools(windowId: number): TPromise<void> {
		const vscodeWindow = this.windowsMainService.getWindowById(windowId);

		if (vscodeWindow) {
			vscodeWindow.win.webContents.openDevTools();
		}

		return TPromise.as(null);
	}

J
Joao Moreno 已提交
89 90 91 92
	toggleDevTools(windowId: number): TPromise<void> {
		const vscodeWindow = this.windowsMainService.getWindowById(windowId);

		if (vscodeWindow) {
93 94 95 96 97 98
			const contents = vscodeWindow.win.webContents;
			if (vscodeWindow.hasHiddenTitleBarStyle() && !vscodeWindow.win.isFullScreen() && !contents.isDevToolsOpened()) {
				contents.openDevTools({ mode: 'undocked' }); // due to https://github.com/electron/electron/issues/3647
			} else {
				contents.toggleDevTools();
			}
J
Joao Moreno 已提交
99 100 101 102
		}

		return TPromise.as(null);
	}
103

J
Joao Moreno 已提交
104 105 106 107
	closeFolder(windowId: number): TPromise<void> {
		const vscodeWindow = this.windowsMainService.getWindowById(windowId);

		if (vscodeWindow) {
C
chrmarti 已提交
108
			this.windowsMainService.open({ context: OpenContext.API, cli: this.environmentService.args, forceEmpty: true, windowToUse: vscodeWindow, forceReuseWindow: true });
109 110 111 112
		}

		return TPromise.as(null);
	}
J
Joao Moreno 已提交
113

J
Joao Moreno 已提交
114
	toggleFullScreen(windowId: number): TPromise<void> {
J
Joao Moreno 已提交
115 116 117
		const vscodeWindow = this.windowsMainService.getWindowById(windowId);

		if (vscodeWindow) {
J
Joao Moreno 已提交
118 119 120 121 122 123
			vscodeWindow.toggleFullScreen();
		}

		return TPromise.as(null);
	}

124 125 126 127 128 129 130 131 132 133
	setRepresentedFilename(windowId: number, fileName: string): TPromise<void> {
		const vscodeWindow = this.windowsMainService.getWindowById(windowId);

		if (vscodeWindow) {
			vscodeWindow.win.setRepresentedFilename(fileName);
		}

		return TPromise.as(null);
	}

134 135 136 137 138 139
	addToRecentlyOpen(paths: { path: string, isFile?: boolean }[]): TPromise<void> {
		this.windowsMainService.addToRecentPathsList(paths);

		return TPromise.as(null);
	}

B
Benjamin Pasero 已提交
140 141 142 143 144 145
	removeFromRecentlyOpen(paths: string[]): TPromise<void> {
		this.windowsMainService.removeFromRecentPathsList(paths);

		return TPromise.as(null);
	}

C
22768  
Cristian 已提交
146 147 148 149 150
	clearRecentPathsList(): TPromise<void> {
		this.windowsMainService.clearRecentPathsList();
		return TPromise.as(null);
	}

J
Joao Moreno 已提交
151 152 153 154 155 156 157 158 159 160 161
	getRecentlyOpen(windowId: number): TPromise<{ files: string[]; folders: string[]; }> {
		const vscodeWindow = this.windowsMainService.getWindowById(windowId);

		if (vscodeWindow) {
			const { files, folders } = this.windowsMainService.getRecentPathsList(vscodeWindow.config.workspacePath, vscodeWindow.config.filesToOpen);
			return TPromise.as({ files, folders });
		}

		return TPromise.as({ files: [], folders: [] });
	}

J
Joao Moreno 已提交
162 163 164 165 166 167 168 169 170 171
	focusWindow(windowId: number): TPromise<void> {
		const vscodeWindow = this.windowsMainService.getWindowById(windowId);

		if (vscodeWindow) {
			vscodeWindow.win.focus();
		}

		return TPromise.as(null);
	}

172 173 174 175 176 177 178 179 180 181
	isFocused(windowId: number): TPromise<boolean> {
		const vscodeWindow = this.windowsMainService.getWindowById(windowId);

		if (vscodeWindow) {
			return TPromise.as(vscodeWindow.win.isFocused());
		}

		return TPromise.as(null);
	}

182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
	isMaximized(windowId: number): TPromise<boolean> {
		const vscodeWindow = this.windowsMainService.getWindowById(windowId);

		if (vscodeWindow) {
			return TPromise.as(vscodeWindow.win.isMaximized());
		}

		return TPromise.as(null);
	}

	maximizeWindow(windowId: number): TPromise<void> {
		const vscodeWindow = this.windowsMainService.getWindowById(windowId);

		if (vscodeWindow) {
			vscodeWindow.win.maximize();
		}

		return TPromise.as(null);
	}

	unmaximizeWindow(windowId: number): TPromise<void> {
		const vscodeWindow = this.windowsMainService.getWindowById(windowId);

		if (vscodeWindow) {
			vscodeWindow.win.unmaximize();
		}

		return TPromise.as(null);
	}

J
Joao Moreno 已提交
212 213 214 215 216 217 218 219 220 221
	setDocumentEdited(windowId: number, flag: boolean): TPromise<void> {
		const vscodeWindow = this.windowsMainService.getWindowById(windowId);

		if (vscodeWindow && vscodeWindow.win.isDocumentEdited() !== flag) {
			vscodeWindow.win.setDocumentEdited(flag);
		}

		return TPromise.as(null);
	}

222
	openWindow(paths: string[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean }): TPromise<void> {
J
Joao Moreno 已提交
223 224
		if (!paths || !paths.length) {
			return TPromise.as(null);
J
Joao Moreno 已提交
225 226
		}

C
chrmarti 已提交
227
		this.windowsMainService.open({ context: OpenContext.API, cli: this.environmentService.args, pathsToOpen: paths, forceNewWindow: options && options.forceNewWindow, forceReuseWindow: options && options.forceReuseWindow });
J
Joao Moreno 已提交
228 229
		return TPromise.as(null);
	}
J
Joao Moreno 已提交
230 231

	openNewWindow(): TPromise<void> {
C
chrmarti 已提交
232
		this.windowsMainService.openNewWindow(OpenContext.API);
J
Joao Moreno 已提交
233 234
		return TPromise.as(null);
	}
J
Joao Moreno 已提交
235 236 237 238 239 240 241 242 243 244

	showWindow(windowId: number): TPromise<void> {
		const vscodeWindow = this.windowsMainService.getWindowById(windowId);

		if (vscodeWindow) {
			vscodeWindow.win.show();
		}

		return TPromise.as(null);
	}
J
Joao Moreno 已提交
245 246 247 248 249 250

	getWindows(): TPromise<{ id: number; path: string; title: string; }[]> {
		const windows = this.windowsMainService.getWindows();
		const result = windows.map(w => ({ path: w.openedWorkspacePath, title: w.win.getTitle(), id: w.id }));
		return TPromise.as(result);
	}
J
Joao Moreno 已提交
251

252 253 254 255
	getWindowCount(): TPromise<number> {
		return TPromise.as(this.windowsMainService.getWindows().length);
	}

J
Joao Moreno 已提交
256 257 258 259
	log(severity: string, ...messages: string[]): TPromise<void> {
		console[severity].apply(console, ...messages);
		return TPromise.as(null);
	}
260 261 262 263 264 265 266 267 268 269

	closeExtensionHostWindow(extensionDevelopmentPath: string): TPromise<void> {
		const windowOnExtension = this.windowsMainService.findWindow(null, null, extensionDevelopmentPath);

		if (windowOnExtension) {
			windowOnExtension.win.close();
		}

		return TPromise.as(null);
	}
J
Joao Moreno 已提交
270 271 272 273 274

	showItemInFolder(path: string): TPromise<void> {
		shell.showItemInFolder(path);
		return TPromise.as(null);
	}
J
Joao Moreno 已提交
275

276 277
	openExternal(url: string): TPromise<boolean> {
		return TPromise.as(shell.openExternal(url));
J
Joao Moreno 已提交
278
	}
279 280 281 282 283

	startCrashReporter(config: Electron.CrashReporterStartOptions): TPromise<void> {
		crashReporter.start(config);
		return TPromise.as(null);
	}
284

285 286 287 288 289
	quit(): TPromise<void> {
		this.windowsMainService.quit();
		return TPromise.as(null);
	}

J
Johannes Rieken 已提交
290
	relaunch(options: { addArgs?: string[], removeArgs?: string[] }): TPromise<void> {
291 292
		this.lifecycleService.relaunch(options);

J
Johannes Rieken 已提交
293 294 295
		return TPromise.as(null);
	}

296 297 298 299
	whenSharedProcessReady(): TPromise<void> {
		return this.sharedProcess.whenReady();
	}

300 301 302 303 304
	toggleSharedProcess(): TPromise<void> {
		this.sharedProcess.toggle();
		return TPromise.as(null);
	}

305 306 307 308
	private openFileForURI(filePath: string): TPromise<void> {
		const cli = assign(Object.create(null), this.environmentService.args, { goto: true });
		const pathsToOpen = [filePath];

C
chrmarti 已提交
309
		this.windowsMainService.open({ context: OpenContext.API, cli, pathsToOpen });
310 311 312
		return TPromise.as(null);
	}

J
jordanmkasla2009 已提交
313
	private parseURIForOpen(uri: URI): string {
314 315 316 317 318 319 320 321 322
		/**
		 * opening vscode://file/drive/path/to/project passes a POSIX-style path on Windows.
		 * i.e vscode://file/c/path/to/project gives a path of /c/path/to/project
		 * let's strip the root slash and ensure the drive letter is something Windows
		 * can understand.
		 */
		if (uri.authority === 'file' && process.platform === 'win32') {
			let path = uri.path.substr(1); // strip the root slash
			let drive = path.slice(0, path.indexOf('/')); // find the drive letter
J
jordanmkasla2009 已提交
323
			if (drive.length === 1) { // add a colon if the uri.path contains a valid drive letter.
J
jordanmkasla2009 已提交
324
				path = path.slice(0, path.indexOf('/')) + ':' + path.slice(path.indexOf('/'));
J
jordanmkasla2009 已提交
325
				return path;
J
jordanmkasla2009 已提交
326
			}
J
jordanmkasla2009 已提交
327 328 329
			if (drive.length === 2 && drive.indexOf(':')) { // path has a colon already
				return path;
			}
J
jordanmkasla2009 已提交
330
			return uri.path;
J
jordanmkasla2009 已提交
331
		}
332 333

		// *NIX platforms can process this path natively.
J
jordanmkasla2009 已提交
334 335 336
		return uri.path;
	}

337 338 339
	dispose(): void {
		this.disposables = dispose(this.disposables);
	}
340
}