src/application-delegate.js
const { ipcRenderer, remote, shell } = require('electron');
const ipcHelpers = require('./ipc-helpers');
const { Emitter, Disposable } = require('event-kit');
const getWindowLoadSettings = require('./get-window-load-settings');
module.exports = class ApplicationDelegate {
constructor() {
this.pendingSettingsUpdateCount = 0;
this._ipcMessageEmitter = null;
}
ipcMessageEmitter() {
if (!this._ipcMessageEmitter) {
this._ipcMessageEmitter = new Emitter();
ipcRenderer.on('message', (event, message, detail) => {
this._ipcMessageEmitter.emit(message, detail);
});
}
return this._ipcMessageEmitter;
}
getWindowLoadSettings() {
return getWindowLoadSettings();
}
open(params) {
return ipcRenderer.send('open', params);
}
pickFolder(callback) {
const responseChannel = 'atom-pick-folder-response';
ipcRenderer.on(responseChannel, function(event, path) {
ipcRenderer.removeAllListeners(responseChannel);
return callback(path);
});
return ipcRenderer.send('pick-folder', responseChannel);
}
getCurrentWindow() {
return remote.getCurrentWindow();
}
closeWindow() {
return ipcHelpers.call('window-method', 'close');
}
async getTemporaryWindowState() {
const stateJSON = await ipcHelpers.call('get-temporary-window-state');
return stateJSON && JSON.parse(stateJSON);
}
setTemporaryWindowState(state) {
return ipcHelpers.call('set-temporary-window-state', JSON.stringify(state));
}
getWindowSize() {
const [width, height] = Array.from(remote.getCurrentWindow().getSize());
return { width, height };
}
setWindowSize(width, height) {
return ipcHelpers.call('set-window-size', width, height);
}
getWindowPosition() {
const [x, y] = Array.from(remote.getCurrentWindow().getPosition());
return { x, y };
}
setWindowPosition(x, y) {
return ipcHelpers.call('set-window-position', x, y);
}
centerWindow() {
return ipcHelpers.call('center-window');
}
focusWindow() {
return ipcHelpers.call('focus-window');
}
showWindow() {
return ipcHelpers.call('show-window');
}
hideWindow() {
return ipcHelpers.call('hide-window');
}
reloadWindow() {
return ipcHelpers.call('window-method', 'reload');
}
restartApplication() {
return ipcRenderer.send('restart-application');
}
minimizeWindow() {
return ipcHelpers.call('window-method', 'minimize');
}
isWindowMaximized() {
return remote.getCurrentWindow().isMaximized();
}
maximizeWindow() {
return ipcHelpers.call('window-method', 'maximize');
}
unmaximizeWindow() {
return ipcHelpers.call('window-method', 'unmaximize');
}
isWindowFullScreen() {
return remote.getCurrentWindow().isFullScreen();
}
setWindowFullScreen(fullScreen = false) {
return ipcHelpers.call('window-method', 'setFullScreen', fullScreen);
}
onDidEnterFullScreen(callback) {
return ipcHelpers.on(ipcRenderer, 'did-enter-full-screen', callback);
}
onDidLeaveFullScreen(callback) {
return ipcHelpers.on(ipcRenderer, 'did-leave-full-screen', callback);
}
async openWindowDevTools() {
// Defer DevTools interaction to the next tick, because using them during
// event handling causes some wrong input events to be triggered on
// `TextEditorComponent` (Ref.: https://github.com/atom/atom/issues/9697).
await new Promise(process.nextTick);
return ipcHelpers.call('window-method', 'openDevTools');
}
async closeWindowDevTools() {
// Defer DevTools interaction to the next tick, because using them during
// event handling causes some wrong input events to be triggered on
// `TextEditorComponent` (Ref.: https://github.com/atom/atom/issues/9697).
await new Promise(process.nextTick);
return ipcHelpers.call('window-method', 'closeDevTools');
}
async toggleWindowDevTools() {
// Defer DevTools interaction to the next tick, because using them during
// event handling causes some wrong input events to be triggered on
// `TextEditorComponent` (Ref.: https://github.com/atom/atom/issues/9697).
await new Promise(process.nextTick);
return ipcHelpers.call('window-method', 'toggleDevTools');
}
executeJavaScriptInWindowDevTools(code) {
return ipcRenderer.send('execute-javascript-in-dev-tools', code);
}
didClosePathWithWaitSession(path) {
return ipcHelpers.call(
'window-method',
'didClosePathWithWaitSession',
path
);
}
setWindowDocumentEdited(edited) {
return ipcHelpers.call('window-method', 'setDocumentEdited', edited);
}
setRepresentedFilename(filename) {
return ipcHelpers.call('window-method', 'setRepresentedFilename', filename);
}
addRecentDocument(filename) {
return ipcRenderer.send('add-recent-document', filename);
}
setProjectRoots(paths) {
return ipcHelpers.call('window-method', 'setProjectRoots', paths);
}
setAutoHideWindowMenuBar(autoHide) {
return ipcHelpers.call('window-method', 'setAutoHideMenuBar', autoHide);
}
setWindowMenuBarVisibility(visible) {
return remote.getCurrentWindow().setMenuBarVisibility(visible);
}
getPrimaryDisplayWorkAreaSize() {
return remote.screen.getPrimaryDisplay().workAreaSize;
}
getUserDefault(key, type) {
return remote.systemPreferences.getUserDefault(key, type);
}
async setUserSettings(config, configFilePath) {
this.pendingSettingsUpdateCount++;
try {
await ipcHelpers.call(
'set-user-settings',
JSON.stringify(config),
configFilePath
);
} finally {
this.pendingSettingsUpdateCount--;
}
}
onDidChangeUserSettings(callback) {
return this.ipcMessageEmitter().on('did-change-user-settings', detail => {
if (this.pendingSettingsUpdateCount === 0) callback(detail);
});
}
onDidFailToReadUserSettings(callback) {
return this.ipcMessageEmitter().on(
'did-fail-to-read-user-setting',
callback
);
}
confirm(options, callback) {
if (typeof callback === 'function') {
// Async version: pass options directly to Electron but set sane defaults
options = Object.assign(
{ type: 'info', normalizeAccessKeys: true },
options
);
remote.dialog
.showMessageBox(remote.getCurrentWindow(), options)
.then(result => {
callback(result.response, result.checkboxChecked);
});
} else {
// Legacy sync version: options can only have `message`,
// `detailedMessage` (optional), and buttons array or object (optional)
let { message, detailedMessage, buttons } = options;
let buttonLabels;
if (!buttons) buttons = {};
if (Array.isArray(buttons)) {
buttonLabels = buttons;
} else {
buttonLabels = Object.keys(buttons);
}
const chosen = remote.dialog.showMessageBoxSync(
remote.getCurrentWindow(),
{
type: 'info',
message,
detail: detailedMessage,
buttons: buttonLabels,
normalizeAccessKeys: true
}
);
if (Array.isArray(buttons)) {
return chosen;
} else {
const callback = buttons[buttonLabels[chosen]];
if (typeof callback === 'function') return callback();
}
}
}
showMessageDialog(params) {}
showSaveDialog(options, callback) {
if (typeof callback === 'function') {
// Async
this.getCurrentWindow().showSaveDialog(options, callback);
} else {
// Sync
if (typeof options === 'string') {
options = { defaultPath: options };
}
return this.getCurrentWindow().showSaveDialog(options);
}
}
playBeepSound() {
return shell.beep();
}
onDidOpenLocations(callback) {
return this.ipcMessageEmitter().on('open-locations', callback);
}
onUpdateAvailable(callback) {
// TODO: Yes, this is strange that `onUpdateAvailable` is listening for
// `did-begin-downloading-update`. We currently have no mechanism to know
// if there is an update, so begin of downloading is a good proxy.
return this.ipcMessageEmitter().on(
'did-begin-downloading-update',
callback
);
}
onDidBeginDownloadingUpdate(callback) {
return this.onUpdateAvailable(callback);
}
onDidBeginCheckingForUpdate(callback) {
return this.ipcMessageEmitter().on('checking-for-update', callback);
}
onDidCompleteDownloadingUpdate(callback) {
return this.ipcMessageEmitter().on('update-available', callback);
}
onUpdateNotAvailable(callback) {
return this.ipcMessageEmitter().on('update-not-available', callback);
}
onUpdateError(callback) {
return this.ipcMessageEmitter().on('update-error', callback);
}
onApplicationMenuCommand(handler) {
const outerCallback = (event, ...args) => handler(...args);
ipcRenderer.on('command', outerCallback);
return new Disposable(() =>
ipcRenderer.removeListener('command', outerCallback)
);
}
onContextMenuCommand(handler) {
const outerCallback = (event, ...args) => handler(...args);
ipcRenderer.on('context-command', outerCallback);
return new Disposable(() =>
ipcRenderer.removeListener('context-command', outerCallback)
);
}
onURIMessage(handler) {
const outerCallback = (event, ...args) => handler(...args);
ipcRenderer.on('uri-message', outerCallback);
return new Disposable(() =>
ipcRenderer.removeListener('uri-message', outerCallback)
);
}
onDidRequestUnload(callback) {
const outerCallback = async (event, message) => {
const shouldUnload = await callback(event);
ipcRenderer.send('did-prepare-to-unload', shouldUnload);
};
ipcRenderer.on('prepare-to-unload', outerCallback);
return new Disposable(() =>
ipcRenderer.removeListener('prepare-to-unload', outerCallback)
);
}
onDidChangeHistoryManager(callback) {
const outerCallback = (event, message) => callback(event);
ipcRenderer.on('did-change-history-manager', outerCallback);
return new Disposable(() =>
ipcRenderer.removeListener('did-change-history-manager', outerCallback)
);
}
didChangeHistoryManager() {
return ipcRenderer.send('did-change-history-manager');
}
openExternal(url) {
return shell.openExternal(url);
}
checkForUpdate() {
return ipcRenderer.send('command', 'application:check-for-update');
}
restartAndInstallUpdate() {
return ipcRenderer.send('command', 'application:install-update');
}
getAutoUpdateManagerState() {
return ipcRenderer.sendSync('get-auto-update-manager-state');
}
getAutoUpdateManagerErrorMessage() {
return ipcRenderer.sendSync('get-auto-update-manager-error');
}
emitWillSavePath(path) {
return ipcHelpers.call('will-save-path', path);
}
emitDidSavePath(path) {
return ipcHelpers.call('did-save-path', path);
}
resolveProxy(requestId, url) {
return ipcRenderer.send('resolve-proxy', requestId, url);
}
onDidResolveProxy(callback) {
const outerCallback = (event, requestId, proxy) =>
callback(requestId, proxy);
ipcRenderer.on('did-resolve-proxy', outerCallback);
return new Disposable(() =>
ipcRenderer.removeListener('did-resolve-proxy', outerCallback)
);
}
};