mirror of
https://github.com/atom/atom.git
synced 2026-02-12 07:35:14 -05:00
232 lines
7.1 KiB
CoffeeScript
232 lines
7.1 KiB
CoffeeScript
AtomWindow = require './atom-window'
|
|
BrowserWindow = require 'browser-window'
|
|
Menu = require 'menu'
|
|
app = require 'app'
|
|
ipc = require 'ipc'
|
|
dialog = require 'dialog'
|
|
fs = require 'fs'
|
|
path = require 'path'
|
|
net = require 'net'
|
|
|
|
socketPath = '/tmp/atom.sock'
|
|
|
|
module.exports =
|
|
class AtomApplication
|
|
@open: (options) ->
|
|
client = net.connect {path: socketPath}, ->
|
|
client.write(JSON.stringify(options))
|
|
app.terminate()
|
|
|
|
client.on 'error', -> new AtomApplication(options)
|
|
|
|
windows: null
|
|
configWindow: null
|
|
menu: null
|
|
resourcePath: null
|
|
pathsToOpen: null
|
|
version: null
|
|
|
|
constructor: ({@resourcePath, @pathsToOpen, @version, test, pidToKillWhenClosed}) ->
|
|
global.atomApplication = this
|
|
|
|
@pidsToOpenWindows = {}
|
|
@pathsToOpen ?= []
|
|
@windows = []
|
|
|
|
@listenForArgumentsFromNewProcess()
|
|
@setupNodePath()
|
|
@setupJavaScriptArguments()
|
|
@buildApplicationMenu()
|
|
@handleEvents()
|
|
|
|
if test
|
|
@runSpecs(true)
|
|
else if @pathsToOpen.length > 0
|
|
@openPaths(@pathsToOpen, pidToKillWhenClosed)
|
|
else
|
|
# Always open a editor window if this is the first instance of Atom.
|
|
@openPath(null)
|
|
|
|
removeWindow: (window) ->
|
|
@windows.splice @windows.indexOf(window), 1
|
|
|
|
addWindow: (window) ->
|
|
@windows.push window
|
|
|
|
getHomeDir: ->
|
|
process.env[if process.platform is 'win32' then 'USERPROFILE' else 'HOME']
|
|
|
|
setupNodePath: ->
|
|
resourcePaths = [
|
|
'src/stdlib'
|
|
'src/app'
|
|
'src/packages'
|
|
'src'
|
|
'vendor'
|
|
'static'
|
|
'node_modules'
|
|
'spec'
|
|
''
|
|
]
|
|
|
|
resourcePaths.push path.join(@getHomeDir(), '.atom', 'packages')
|
|
|
|
resourcePaths = resourcePaths.map (relativeOrAbsolutePath) =>
|
|
path.resolve @resourcePath, relativeOrAbsolutePath
|
|
|
|
process.env['NODE_PATH'] = resourcePaths.join path.delimiter
|
|
|
|
listenForArgumentsFromNewProcess: ->
|
|
fs.unlinkSync socketPath if fs.existsSync(socketPath)
|
|
server = net.createServer (connection) =>
|
|
connection.on 'data', (data) =>
|
|
{pathsToOpen, pidToKillWhenClosed} = JSON.parse(data)
|
|
@openPaths(pathsToOpen, pidToKillWhenClosed)
|
|
|
|
server.listen socketPath
|
|
server.on 'error', (error) -> console.error 'Application server failed', error
|
|
|
|
setupJavaScriptArguments: ->
|
|
app.commandLine.appendSwitch 'js-flags', '--harmony_collections'
|
|
|
|
buildApplicationMenu: ->
|
|
atomMenu =
|
|
label: 'Atom'
|
|
submenu: [
|
|
{ label: 'About Atom', selector: 'orderFrontStandardAboutPanel:' }
|
|
{ label: "Version #{@version}", enabled: false }
|
|
{ type: 'separator' }
|
|
{ label: 'Preferences...', accelerator: 'Command+,', click: => @openConfig() }
|
|
{ type: 'separator' }
|
|
{ label: 'Hide Atom Shell', accelerator: 'Command+H', selector: 'hide:' }
|
|
{ label: 'Hide Others', accelerator: 'Command+Shift+H', selector: 'hideOtherApplications:' }
|
|
{ label: 'Show All', selector: 'unhideAllApplications:' }
|
|
{ type: 'separator' }
|
|
{ label: 'Run Specs', accelerator: 'Command+MacCtrl+Alt+S', click: => @runSpecs() }
|
|
{ type: 'separator' }
|
|
{ label: 'Quit', accelerator: 'Command+Q', click: -> app.quit() }
|
|
]
|
|
|
|
editMenu =
|
|
label: 'Edit'
|
|
submenu:[
|
|
{ label: 'Undo', accelerator: 'Command+Z', selector: 'undo:' }
|
|
{ label: 'Redo', accelerator: 'Command+Shift+Z', selector: 'redo:' }
|
|
{ type: 'separator' }
|
|
{ label: 'Cut', accelerator: 'Command+X', selector: 'cut:' }
|
|
{ label: 'Copy', accelerator: 'Command+C', selector: 'copy:' }
|
|
{ label: 'Paste', accelerator: 'Command+V', selector: 'paste:' }
|
|
{ label: 'Select All', accelerator: 'Command+A', selector: 'selectAll:' }
|
|
]
|
|
|
|
viewMenu =
|
|
label: 'View'
|
|
submenu:[
|
|
{ label: 'Reload', accelerator: 'Command+R', click: => BrowserWindow.getFocusedWindow()?.restart() }
|
|
{ label: 'Toggle DevTools', accelerator: 'Alt+Command+I', click: => BrowserWindow.getFocusedWindow()?.toggleDevTools() }
|
|
]
|
|
|
|
windowMenu =
|
|
label: 'Window'
|
|
submenu: [
|
|
{ label: 'Minimize', accelerator: 'Command+M', selector: 'performMiniaturize:' }
|
|
{ label: 'Close', accelerator: 'Command+W', selector: 'performClose:' }
|
|
{ type: 'separator' }
|
|
{ label: 'Bring All to Front', selector: 'arrangeInFront:' }
|
|
]
|
|
|
|
@menu = Menu.buildFromTemplate [atomMenu, viewMenu, editMenu, windowMenu]
|
|
Menu.setApplicationMenu @menu
|
|
|
|
handleEvents: ->
|
|
# Quit when all windows are closed.
|
|
app.on 'window-all-closed', ->
|
|
app.quit()
|
|
|
|
# Clean the socket file when quit normally.
|
|
app.on 'will-quit', =>
|
|
fs.unlinkSync socketPath if fs.existsSync(socketPath)
|
|
|
|
app.on 'open-file', (event, filePath) =>
|
|
event.preventDefault()
|
|
@openPath filePath
|
|
|
|
|
|
ipc.on 'close-without-confirm', (processId, routingId) ->
|
|
window = BrowserWindow.fromProcessIdAndRoutingId processId, routingId
|
|
window.removeAllListeners 'close'
|
|
window.close()
|
|
|
|
ipc.on 'open-config', =>
|
|
@openConfig()
|
|
|
|
ipc.on 'open', (processId, routingId, pathsToOpen) =>
|
|
if pathsToOpen?.length > 0
|
|
@openPaths(pathsToOpen)
|
|
else
|
|
pathsToOpen = dialog.showOpenDialog title: 'Open', properties: ['openFile', 'openDirectory', 'multiSelections', 'createDirectory']
|
|
@openPaths(pathsToOpen)
|
|
|
|
ipc.on 'new-window', =>
|
|
@open()
|
|
|
|
ipc.on 'get-version', (event) =>
|
|
event.result = @version
|
|
|
|
sendCommand: (command, args...) ->
|
|
for atomWindow in @windows when atomWindow.isFocused()
|
|
atomWindow.sendCommand(command, args...)
|
|
|
|
windowForPath: (pathToOpen) ->
|
|
return null unless pathToOpen
|
|
|
|
for atomWindow in @windows when atomWindow.pathToOpen?
|
|
if pathToOpen is atomWindow.pathToOpen
|
|
return atomWindow
|
|
|
|
if pathToOpen.indexOf(path.join(atomWindow.pathToOpen, path.sep)) is 0
|
|
return atomWindow
|
|
|
|
null
|
|
|
|
openPaths: (pathsToOpen=[], pidToKillWhenClosed) ->
|
|
@openPath(pathToOpen, pidToKillWhenClosed) for pathToOpen in pathsToOpen
|
|
|
|
openPath: (pathToOpen, pidToKillWhenClosed) ->
|
|
existingWindow = @windowForPath(pathToOpen) unless pidToKillWhenClosed
|
|
if existingWindow
|
|
openedWindow = existingWindow
|
|
openedWindow.focus()
|
|
openedWindow.sendCommand('window:open-path', pathToOpen)
|
|
else
|
|
bootstrapScript = 'window-bootstrap'
|
|
openedWindow = new AtomWindow({pathToOpen, bootstrapScript, @resourcePath})
|
|
|
|
if pidToKillWhenClosed?
|
|
@pidsToOpenWindows[pidToKillWhenClosed] = openedWindow
|
|
|
|
openedWindow.browserWindow.on 'closed', =>
|
|
for pid, trackedWindow of @pidsToOpenWindows when trackedWindow is openedWindow
|
|
process.kill(pid)
|
|
delete @pidsToOpenWindows[pid]
|
|
|
|
openConfig: ->
|
|
if @configWindow
|
|
@configWindow.focus()
|
|
return
|
|
|
|
@configWindow = new AtomWindow
|
|
bootstrapScript: 'config-bootstrap'
|
|
resourcePath: @resourcePath
|
|
@configWindow.browserWindow.on 'destroyed', =>
|
|
@configWindow = null
|
|
|
|
runSpecs: (exitWhenDone) ->
|
|
specWindow = new AtomWindow
|
|
bootstrapScript: 'spec-bootstrap'
|
|
resourcePath: @resourcePath
|
|
exitWhenDone: exitWhenDone
|
|
isSpec: true
|
|
|
|
specWindow.show()
|