const {ipcRenderer, remote, shell} = require('electron') const ipcHelpers = require('./ipc-helpers') const {Disposable} = require('event-kit') const getWindowLoadSettings = require('./get-window-load-settings') module.exports = class ApplicationDelegate { constructor () { this.pendingSettingsUpdateCount = 0 } 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 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) } setRepresentedDirectoryPaths (paths) { return ipcHelpers.call('window-method', 'setRepresentedDirectoryPaths', 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) { const outerCallback = (event, message, detail) => { if (message === 'did-change-user-settings') { if (this.pendingSettingsUpdateCount === 0) callback(detail) } } ipcRenderer.on('message', outerCallback) return new Disposable(() => ipcRenderer.removeListener('message', outerCallback)) } onDidFailToReadUserSettings (callback) { const outerCallback = (event, message, detail) => { if (message === 'did-fail-to-read-user-settings') callback(detail) } ipcRenderer.on('message', outerCallback) return new Disposable(() => ipcRenderer.removeListener('message', outerCallback)) } 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, callback) } 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.showMessageBox(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) { const outerCallback = (event, message, detail) => { if (message === 'open-locations') callback(detail) } ipcRenderer.on('message', outerCallback) return new Disposable(() => ipcRenderer.removeListener('message', outerCallback)) } onUpdateAvailable (callback) { const outerCallback = (event, message, detail) => { // 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. if (message === 'did-begin-downloading-update') callback(detail) } ipcRenderer.on('message', outerCallback) return new Disposable(() => ipcRenderer.removeListener('message', outerCallback)) } onDidBeginDownloadingUpdate (callback) { return this.onUpdateAvailable(callback) } onDidBeginCheckingForUpdate (callback) { const outerCallback = (event, message, detail) => { if (message === 'checking-for-update') callback(detail) } ipcRenderer.on('message', outerCallback) return new Disposable(() => ipcRenderer.removeListener('message', outerCallback)) } onDidCompleteDownloadingUpdate (callback) { const outerCallback = (event, message, detail) => { // TODO: We could rename this event to `did-complete-downloading-update` if (message === 'update-available') callback(detail) } ipcRenderer.on('message', outerCallback) return new Disposable(() => ipcRenderer.removeListener('message', outerCallback)) } onUpdateNotAvailable (callback) { const outerCallback = (event, message, detail) => { if (message === 'update-not-available') callback(detail) } ipcRenderer.on('message', outerCallback) return new Disposable(() => ipcRenderer.removeListener('message', outerCallback)) } onUpdateError (callback) { const outerCallback = (event, message, detail) => { if (message === 'update-error') callback(detail) } ipcRenderer.on('message', outerCallback) return new Disposable(() => ipcRenderer.removeListener('message', outerCallback)) } 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)) } }