Files
atom/src/atom-application.coffee
2013-05-30 12:02:56 -07:00

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()