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

'use strict';

8
import nls = require('vs/nls');
E
Erich Gamma 已提交
9 10 11
import {TPromise} from 'vs/base/common/winjs.base';
import errors = require('vs/base/common/errors');
import arrays = require('vs/base/common/arrays');
12
import Severity from 'vs/base/common/severity';
13 14
import {Separator} from 'vs/base/browser/ui/actionbar/actionbar';
import {IAction, Action} from 'vs/base/common/actions';
E
Erich Gamma 已提交
15
import {IPartService} from 'vs/workbench/services/part/common/partService';
B
Benjamin Pasero 已提交
16
import {IMessageService} from 'vs/platform/message/common/message';
E
Erich Gamma 已提交
17 18
import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation';
import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry';
19
import {IContextMenuService} from 'vs/platform/contextview/browser/contextView';
20
import {ICommandService} from 'vs/platform/commands/common/commands';
21
import {IKeybindingService} from 'vs/platform/keybinding/common/keybinding';
E
Erich Gamma 已提交
22
import {IWorkspaceContextService}from 'vs/workbench/services/workspace/common/contextService';
B
polish  
Benjamin Pasero 已提交
23
import {IWindowService} from 'vs/workbench/services/window/electron-browser/windowService';
B
Benjamin Pasero 已提交
24
import {IWindowConfiguration} from 'vs/workbench/electron-browser/window';
25
import {IConfigurationService} from 'vs/platform/configuration/common/configuration';
B
polish  
Benjamin Pasero 已提交
26
import {ElectronWindow} from 'vs/workbench/electron-browser/window';
27
import * as browser from 'vs/base/browser/browser';
E
Erich Gamma 已提交
28

B
Benjamin Pasero 已提交
29
import {ipcRenderer as ipc, webFrame, remote} from 'electron';
E
Erich Gamma 已提交
30

31 32
const currentWindow = remote.getCurrentWindow();

33 34 35 36 37 38 39 40 41 42 43
const TextInputActions: IAction[] = [
	new Action('undo', nls.localize('undo', "Undo"), null, true, () => document.execCommand('undo') && TPromise.as(true)),
	new Action('redo', nls.localize('redo', "Redo"), null, true, () => document.execCommand('redo') && TPromise.as(true)),
	new Separator(),
	new Action('editor.action.clipboardCutAction', nls.localize('cut', "Cut"), null, true, () => document.execCommand('cut') && TPromise.as(true)),
	new Action('editor.action.clipboardCopyAction', nls.localize('copy', "Copy"), null, true, () => document.execCommand('copy') && TPromise.as(true)),
	new Action('editor.action.clipboardPasteAction', nls.localize('paste', "Paste"), null, true, () => document.execCommand('paste') && TPromise.as(true)),
	new Separator(),
	new Action('editor.action.selectAll', nls.localize('selectAll', "Select All"), null, true, () => document.execCommand('selectAll') && TPromise.as(true))
];

E
Erich Gamma 已提交
44 45 46 47 48 49 50 51
export class ElectronIntegration {

	constructor(
		@IInstantiationService private instantiationService: IInstantiationService,
		@IWindowService private windowService: IWindowService,
		@IPartService private partService: IPartService,
		@IWorkspaceContextService private contextService: IWorkspaceContextService,
		@ITelemetryService private telemetryService: ITelemetryService,
B
Benjamin Pasero 已提交
52
		@IConfigurationService private configurationService: IConfigurationService,
53
		@ICommandService private commandService: ICommandService,
E
Erich Gamma 已提交
54
		@IKeybindingService private keybindingService: IKeybindingService,
55 56
		@IMessageService private messageService: IMessageService,
		@IContextMenuService private contextMenuService: IContextMenuService
E
Erich Gamma 已提交
57 58 59 60 61 62
	) {
	}

	public integrate(shellContainer: HTMLElement): void {

		// Register the active window
B
polish  
Benjamin Pasero 已提交
63
		let activeWindow = this.instantiationService.createInstance(ElectronWindow, currentWindow, shellContainer);
E
Erich Gamma 已提交
64 65 66
		this.windowService.registerWindow(activeWindow);

		// Support runAction event
67
		ipc.on('vscode:runAction', (event, actionId: string) => {
68
			this.commandService.executeCommand(actionId, { from: 'menu' }).done(undefined, err => this.messageService.show(Severity.Error, err));
E
Erich Gamma 已提交
69 70 71
		});

		// Support options change
72
		ipc.on('vscode:optionsChange', (event, options: string) => {
E
Erich Gamma 已提交
73 74 75 76 77 78 79 80 81 82
			let optionsData = JSON.parse(options);
			for (let key in optionsData) {
				if (optionsData.hasOwnProperty(key)) {
					let value = optionsData[key];
					this.contextService.updateOptions(key, value);
				}
			}
		});

		// Support resolve keybindings event
83
		ipc.on('vscode:resolveKeybindings', (event, rawActionIds: string) => {
E
Erich Gamma 已提交
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
			let actionIds: string[] = [];
			try {
				actionIds = JSON.parse(rawActionIds);
			} catch (error) {
				// should not happen
			}

			// Resolve keys using the keybinding service and send back to browser process
			this.resolveKeybindings(actionIds).done((keybindings) => {
				if (keybindings.length) {
					ipc.send('vscode:keybindingsResolved', JSON.stringify(keybindings));
				}
			}, () => errors.onUnexpectedError);
		});

99
		ipc.on('vscode:telemetry', (event, { eventName, data }) => {
E
Erich Gamma 已提交
100 101 102
			this.telemetryService.publicLog(eventName, data);
		});

103
		ipc.on('vscode:reportError', (event, error) => {
E
Erich Gamma 已提交
104 105 106 107 108 109 110 111 112 113 114 115
			if (error) {
				let errorParsed = JSON.parse(error);
				errorParsed.mainProcess = true;
				errors.onUnexpectedError(errorParsed);
			}
		});

		// Emit event when vscode has loaded
		this.partService.joinCreation().then(() => {
			ipc.send('vscode:workbenchLoaded', this.windowService.getWindowId());
		});

116 117 118 119 120
		// Message support
		ipc.on('vscode:showInfoMessage', (event, message: string) => {
			this.messageService.show(Severity.Info, message);
		});

121 122 123
		// Ensure others can listen to zoom level changes
		browser.setZoomLevel(webFrame.getZoomLevel());

B
Benjamin Pasero 已提交
124
		// Configuration changes
125
		let previousConfiguredZoomLevel: number;
126
		this.configurationService.onDidUpdateConfiguration(e => {
B
Benjamin Pasero 已提交
127 128 129 130 131
			let windowConfig: IWindowConfiguration = e.config;

			let newZoomLevel = 0;
			if (windowConfig.window && typeof windowConfig.window.zoomLevel === 'number') {
				newZoomLevel = windowConfig.window.zoomLevel;
132 133 134 135 136 137 138

				// Leave early if the configured zoom level did not change (https://github.com/Microsoft/vscode/issues/1536)
				if (previousConfiguredZoomLevel === newZoomLevel) {
					return;
				}

				previousConfiguredZoomLevel = newZoomLevel;
B
Benjamin Pasero 已提交
139 140 141 142
			}

			if (webFrame.getZoomLevel() !== newZoomLevel) {
				webFrame.setZoomLevel(newZoomLevel);
B
Benjamin Pasero 已提交
143
				browser.setZoomLevel(webFrame.getZoomLevel()); // Ensure others can listen to zoom level changes
B
Benjamin Pasero 已提交
144
			}
B
Benjamin Pasero 已提交
145
		});
146

147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
		// Context menu support in input/textarea
		window.document.addEventListener('contextmenu', (e) => {
			if (e.target instanceof HTMLElement) {
				const target = <HTMLElement>e.target;
				if (target.nodeName && (target.nodeName.toLowerCase() === 'input' || target.nodeName.toLowerCase() === 'textarea')) {
					e.preventDefault();
					e.stopPropagation();

					this.contextMenuService.showContextMenu({
						getAnchor: () => target,
						getActions: () => TPromise.as(TextInputActions),
						getKeyBinding: (action) => {
							var opts = this.keybindingService.lookupKeybindings(action.id);
							if (opts.length > 0) {
								return opts[0]; // only take the first one
							}

							return null;
						}
					});
				}
			}
		});
E
Erich Gamma 已提交
170 171 172 173 174 175
	}

	private resolveKeybindings(actionIds: string[]): TPromise<{ id: string; binding: number; }[]> {
		return this.partService.joinCreation().then(() => {
			return arrays.coalesce(actionIds.map((id) => {
				let bindings = this.keybindingService.lookupKeybindings(id);
176 177 178 179 180 181 182 183 184 185 186

				// return the first binding that can be represented by electron
				for (let i = 0; i < bindings.length; i++) {
					let binding = bindings[i];
					let electronAccelerator = this.keybindingService.getElectronAcceleratorFor(binding);
					if (electronAccelerator) {
						return {
							id: id,
							binding: binding.value
						};
					}
E
Erich Gamma 已提交
187 188 189 190 191 192 193
				}

				return null;
			}));
		});
	}
}