windowsService.ts 12.1 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';
B
Benjamin Pasero 已提交
12
import { IWindowsService, OpenContext, INativeOpenDialogOptions } from 'vs/platform/windows/common/windows';
B
Benjamin Pasero 已提交
13
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
14
import { shell, crashReporter, app, Menu } 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 19
import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
import { IWindowsMainService, ISharedProcess } from 'vs/platform/windows/electron-main/windows';
B
Benjamin Pasero 已提交
20 21
import { IHistoryMainService, IRecentlyOpened } from 'vs/platform/history/common/history';
import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
22
import { ICommandAction } from 'vs/platform/actions/common/actions';
23

24
export class WindowsService implements IWindowsService, IDisposable {
J
Joao Moreno 已提交
25 26 27

	_serviceBrand: any;

28 29
	private disposables: IDisposable[] = [];

30 31 32
	readonly onWindowOpen: Event<number> = fromEventEmitter(app, 'browser-window-created', (_, w: Electron.BrowserWindow) => w.id);
	readonly onWindowFocus: Event<number> = fromEventEmitter(app, 'browser-window-focus', (_, w: Electron.BrowserWindow) => w.id);
	readonly onWindowBlur: Event<number> = fromEventEmitter(app, 'browser-window-blur', (_, w: Electron.BrowserWindow) => w.id);
J
Joao Moreno 已提交
33

J
Joao Moreno 已提交
34
	constructor(
35
		private sharedProcess: ISharedProcess,
36
		@IWindowsMainService private windowsMainService: IWindowsMainService,
37
		@IEnvironmentService private environmentService: IEnvironmentService,
38
		@IURLService urlService: IURLService,
39 40
		@ILifecycleService private lifecycleService: ILifecycleService,
		@IHistoryMainService private historyService: IHistoryMainService
41
	) {
42
		// Catch file URLs
43
		chain(urlService.onOpenURL)
44
			.filter(uri => uri.authority === 'file' && !!uri.path)
45
			.map(uri => URI.file(uri.fsPath))
46
			.on(this.openFileForURI, this, this.disposables);
47 48 49 50 51 52

		// Catch extension URLs when there are no windows open
		chain(urlService.onOpenURL)
			.filter(uri => /^extension/.test(uri.path))
			.filter(() => this.windowsMainService.getWindowCount() === 0)
			.on(this.openExtensionForURI, this, this.disposables);
53
	}
J
Joao Moreno 已提交
54

B
Benjamin Pasero 已提交
55 56 57
	pickFileFolderAndOpen(options: INativeOpenDialogOptions): TPromise<void> {
		this.windowsMainService.pickFileFolderAndOpen(options);

J
Joao Moreno 已提交
58 59 60
		return TPromise.as(null);
	}

B
Benjamin Pasero 已提交
61 62 63
	pickFileAndOpen(options: INativeOpenDialogOptions): TPromise<void> {
		this.windowsMainService.pickFileAndOpen(options);

J
Joao Moreno 已提交
64 65 66
		return TPromise.as(null);
	}

B
Benjamin Pasero 已提交
67 68
	pickFolderAndOpen(options: INativeOpenDialogOptions): TPromise<void> {
		this.windowsMainService.pickFolderAndOpen(options);
69

J
Joao Moreno 已提交
70 71
		return TPromise.as(null);
	}
72 73

	reloadWindow(windowId: number): TPromise<void> {
B
Benjamin Pasero 已提交
74
		const codeWindow = this.windowsMainService.getWindowById(windowId);
75

B
Benjamin Pasero 已提交
76 77
		if (codeWindow) {
			this.windowsMainService.reload(codeWindow);
78 79 80 81
		}

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

J
Joao Moreno 已提交
83
	openDevTools(windowId: number): TPromise<void> {
B
Benjamin Pasero 已提交
84
		const codeWindow = this.windowsMainService.getWindowById(windowId);
J
Joao Moreno 已提交
85

B
Benjamin Pasero 已提交
86 87
		if (codeWindow) {
			codeWindow.win.webContents.openDevTools();
J
Joao Moreno 已提交
88 89 90 91 92
		}

		return TPromise.as(null);
	}

J
Joao Moreno 已提交
93
	toggleDevTools(windowId: number): TPromise<void> {
B
Benjamin Pasero 已提交
94
		const codeWindow = this.windowsMainService.getWindowById(windowId);
J
Joao Moreno 已提交
95

B
Benjamin Pasero 已提交
96 97 98
		if (codeWindow) {
			const contents = codeWindow.win.webContents;
			if (codeWindow.hasHiddenTitleBarStyle() && !codeWindow.win.isFullScreen() && !contents.isDevToolsOpened()) {
99 100 101 102
				contents.openDevTools({ mode: 'undocked' }); // due to https://github.com/electron/electron/issues/3647
			} else {
				contents.toggleDevTools();
			}
J
Joao Moreno 已提交
103 104 105 106
		}

		return TPromise.as(null);
	}
107

108 109 110 111 112 113 114 115 116 117
	updateTouchBar(windowId: number, items: ICommandAction[][]): TPromise<void> {
		const codeWindow = this.windowsMainService.getWindowById(windowId);

		if (codeWindow) {
			codeWindow.updateTouchBar(items);
		}

		return TPromise.as(null);
	}

118
	closeWorkspace(windowId: number): TPromise<void> {
B
Benjamin Pasero 已提交
119
		const codeWindow = this.windowsMainService.getWindowById(windowId);
J
Joao Moreno 已提交
120

B
Benjamin Pasero 已提交
121
		if (codeWindow) {
122
			this.windowsMainService.closeWorkspace(codeWindow);
123 124 125 126
		}

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

128 129 130 131 132 133 134
	openWorkspace(windowId: number): TPromise<void> {
		const codeWindow = this.windowsMainService.getWindowById(windowId);

		if (codeWindow) {
			this.windowsMainService.openWorkspace(codeWindow);
		}

135 136 137
		return TPromise.as(null);
	}

138
	createAndEnterWorkspace(windowId: number, folders?: string[], path?: string): TPromise<IWorkspaceIdentifier> {
139 140 141
		const codeWindow = this.windowsMainService.getWindowById(windowId);

		if (codeWindow) {
142
			return this.windowsMainService.createAndEnterWorkspace(codeWindow, folders, path);
143 144
		}

145 146 147
		return TPromise.as(null);
	}

148
	saveAndEnterWorkspace(windowId: number, path: string): TPromise<IWorkspaceIdentifier> {
149 150 151
		const codeWindow = this.windowsMainService.getWindowById(windowId);

		if (codeWindow) {
152
			return this.windowsMainService.saveAndEnterWorkspace(codeWindow, path);
153 154
		}

155 156 157
		return TPromise.as(null);
	}

J
Joao Moreno 已提交
158
	toggleFullScreen(windowId: number): TPromise<void> {
B
Benjamin Pasero 已提交
159
		const codeWindow = this.windowsMainService.getWindowById(windowId);
J
Joao Moreno 已提交
160

B
Benjamin Pasero 已提交
161 162
		if (codeWindow) {
			codeWindow.toggleFullScreen();
J
Joao Moreno 已提交
163 164 165 166 167
		}

		return TPromise.as(null);
	}

168
	setRepresentedFilename(windowId: number, fileName: string): TPromise<void> {
B
Benjamin Pasero 已提交
169
		const codeWindow = this.windowsMainService.getWindowById(windowId);
170

B
Benjamin Pasero 已提交
171 172
		if (codeWindow) {
			codeWindow.setRepresentedFilename(fileName);
173 174 175 176 177
		}

		return TPromise.as(null);
	}

178 179
	addRecentlyOpened(files: string[]): TPromise<void> {
		this.historyService.addRecentlyOpened(void 0, files);
180 181 182 183

		return TPromise.as(null);
	}

184 185
	removeFromRecentlyOpened(paths: string[]): TPromise<void> {
		this.historyService.removeFromRecentlyOpened(paths);
B
Benjamin Pasero 已提交
186 187 188 189

		return TPromise.as(null);
	}

B
Benjamin Pasero 已提交
190 191
	clearRecentlyOpened(): TPromise<void> {
		this.historyService.clearRecentlyOpened();
192

C
22768  
Cristian 已提交
193 194 195
		return TPromise.as(null);
	}

196
	getRecentlyOpened(windowId: number): TPromise<IRecentlyOpened> {
B
Benjamin Pasero 已提交
197
		const codeWindow = this.windowsMainService.getWindowById(windowId);
J
Joao Moreno 已提交
198

B
Benjamin Pasero 已提交
199
		if (codeWindow) {
B
Benjamin Pasero 已提交
200
			return TPromise.as(this.historyService.getRecentlyOpened(codeWindow.config.workspace || codeWindow.config.folderPath, codeWindow.config.filesToOpen));
J
Joao Moreno 已提交
201 202
		}

B
Benjamin Pasero 已提交
203
		return TPromise.as(this.historyService.getRecentlyOpened());
J
Joao Moreno 已提交
204 205
	}

206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
	showPreviousWindowTab(): TPromise<void> {
		Menu.sendActionToFirstResponder('selectPreviousTab:');

		return TPromise.as(void 0);
	}

	showNextWindowTab(): TPromise<void> {
		Menu.sendActionToFirstResponder('selectNextTab:');

		return TPromise.as(void 0);
	}

	moveWindowTabToNewWindow(): TPromise<void> {
		Menu.sendActionToFirstResponder('moveTabToNewWindow:');

		return TPromise.as(void 0);
	}

	mergeAllWindowTabs(): TPromise<void> {
		Menu.sendActionToFirstResponder('mergeAllWindows:');

		return TPromise.as(void 0);
	}

	toggleWindowTabsBar(): TPromise<void> {
		Menu.sendActionToFirstResponder('toggleTabBar:');

		return TPromise.as(void 0);
	}

J
Joao Moreno 已提交
236
	focusWindow(windowId: number): TPromise<void> {
B
Benjamin Pasero 已提交
237
		const codeWindow = this.windowsMainService.getWindowById(windowId);
J
Joao Moreno 已提交
238

B
Benjamin Pasero 已提交
239 240
		if (codeWindow) {
			codeWindow.win.focus();
J
Joao Moreno 已提交
241 242 243 244 245
		}

		return TPromise.as(null);
	}

246 247 248 249 250 251 252 253 254 255
	closeWindow(windowId: number): TPromise<void> {
		const codeWindow = this.windowsMainService.getWindowById(windowId);

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

		return TPromise.as(null);
	}

256
	isFocused(windowId: number): TPromise<boolean> {
B
Benjamin Pasero 已提交
257
		const codeWindow = this.windowsMainService.getWindowById(windowId);
258

B
Benjamin Pasero 已提交
259 260
		if (codeWindow) {
			return TPromise.as(codeWindow.win.isFocused());
261 262 263 264 265
		}

		return TPromise.as(null);
	}

266
	isMaximized(windowId: number): TPromise<boolean> {
B
Benjamin Pasero 已提交
267
		const codeWindow = this.windowsMainService.getWindowById(windowId);
268

B
Benjamin Pasero 已提交
269 270
		if (codeWindow) {
			return TPromise.as(codeWindow.win.isMaximized());
271 272 273 274 275 276
		}

		return TPromise.as(null);
	}

	maximizeWindow(windowId: number): TPromise<void> {
B
Benjamin Pasero 已提交
277
		const codeWindow = this.windowsMainService.getWindowById(windowId);
278

B
Benjamin Pasero 已提交
279 280
		if (codeWindow) {
			codeWindow.win.maximize();
281 282 283 284 285 286
		}

		return TPromise.as(null);
	}

	unmaximizeWindow(windowId: number): TPromise<void> {
B
Benjamin Pasero 已提交
287
		const codeWindow = this.windowsMainService.getWindowById(windowId);
288

B
Benjamin Pasero 已提交
289 290
		if (codeWindow) {
			codeWindow.win.unmaximize();
291 292 293 294 295 296
		}

		return TPromise.as(null);
	}

	onWindowTitleDoubleClick(windowId: number): TPromise<void> {
B
Benjamin Pasero 已提交
297
		const codeWindow = this.windowsMainService.getWindowById(windowId);
298

B
Benjamin Pasero 已提交
299 300
		if (codeWindow) {
			codeWindow.onWindowTitleDoubleClick();
301 302 303 304 305
		}

		return TPromise.as(null);
	}

J
Joao Moreno 已提交
306
	setDocumentEdited(windowId: number, flag: boolean): TPromise<void> {
B
Benjamin Pasero 已提交
307
		const codeWindow = this.windowsMainService.getWindowById(windowId);
J
Joao Moreno 已提交
308

B
Benjamin Pasero 已提交
309 310
		if (codeWindow && codeWindow.win.isDocumentEdited() !== flag) {
			codeWindow.win.setDocumentEdited(flag);
J
Joao Moreno 已提交
311 312 313 314 315
		}

		return TPromise.as(null);
	}

316
	openWindow(paths: string[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean, forceOpenWorkspaceAsFile?: boolean }): TPromise<void> {
J
Joao Moreno 已提交
317 318
		if (!paths || !paths.length) {
			return TPromise.as(null);
J
Joao Moreno 已提交
319 320
		}

321 322 323 324 325 326 327 328 329
		this.windowsMainService.open({
			context: OpenContext.API,
			cli: this.environmentService.args,
			pathsToOpen: paths,
			forceNewWindow: options && options.forceNewWindow,
			forceReuseWindow: options && options.forceReuseWindow,
			forceOpenWorkspaceAsFile: options && options.forceOpenWorkspaceAsFile
		});

J
Joao Moreno 已提交
330 331
		return TPromise.as(null);
	}
J
Joao Moreno 已提交
332 333

	openNewWindow(): TPromise<void> {
C
chrmarti 已提交
334
		this.windowsMainService.openNewWindow(OpenContext.API);
J
Joao Moreno 已提交
335 336
		return TPromise.as(null);
	}
J
Joao Moreno 已提交
337 338

	showWindow(windowId: number): TPromise<void> {
B
Benjamin Pasero 已提交
339
		const codeWindow = this.windowsMainService.getWindowById(windowId);
J
Joao Moreno 已提交
340

B
Benjamin Pasero 已提交
341 342
		if (codeWindow) {
			codeWindow.win.show();
J
Joao Moreno 已提交
343 344 345 346
		}

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

348
	getWindows(): TPromise<{ id: number; workspace?: IWorkspaceIdentifier; folderPath?: string; title: string; filename?: string; }[]> {
J
Joao Moreno 已提交
349
		const windows = this.windowsMainService.getWindows();
350
		const result = windows.map(w => ({ id: w.id, workspace: w.openedWorkspace, openedFolderPath: w.openedFolderPath, title: w.win.getTitle(), filename: w.getRepresentedFilename() }));
351

J
Joao Moreno 已提交
352 353
		return TPromise.as(result);
	}
J
Joao Moreno 已提交
354

355 356 357 358
	getWindowCount(): TPromise<number> {
		return TPromise.as(this.windowsMainService.getWindows().length);
	}

J
Joao Moreno 已提交
359 360 361 362
	log(severity: string, ...messages: string[]): TPromise<void> {
		console[severity].apply(console, ...messages);
		return TPromise.as(null);
	}
363

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

369 370
	openExternal(url: string): TPromise<boolean> {
		return TPromise.as(shell.openExternal(url));
J
Joao Moreno 已提交
371
	}
372 373 374 375 376

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

378 379 380 381 382
	quit(): TPromise<void> {
		this.windowsMainService.quit();
		return TPromise.as(null);
	}

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

J
Johannes Rieken 已提交
386 387 388
		return TPromise.as(null);
	}

389 390 391 392
	whenSharedProcessReady(): TPromise<void> {
		return this.sharedProcess.whenReady();
	}

393 394 395 396 397
	toggleSharedProcess(): TPromise<void> {
		this.sharedProcess.toggle();
		return TPromise.as(null);
	}

398
	private openFileForURI(uri: URI): TPromise<void> {
399
		const cli = assign(Object.create(null), this.environmentService.args, { goto: true });
400
		const pathsToOpen = [uri.fsPath];
401

C
chrmarti 已提交
402
		this.windowsMainService.open({ context: OpenContext.API, cli, pathsToOpen });
403 404 405
		return TPromise.as(null);
	}

406 407 408 409
	/**
	 * This should only fire whenever an extension URL is open
	 * and there are no windows to handle it.
	 */
J
Joao 已提交
410
	private async openExtensionForURI(uri: URI): TPromise<void> {
411
		const cli = assign(Object.create(null), this.environmentService.args);
J
Joao 已提交
412 413 414 415 416 417 418
		const window = await this.windowsMainService.open({ context: OpenContext.API, cli })[0];

		if (!window) {
			return;
		}

		window.win.show();
419 420
	}

421 422 423
	dispose(): void {
		this.disposables = dispose(this.disposables);
	}
424
}